rnd-20030919-1-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "tools.h"
22 #include "tape.h"
23
24
25 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
26 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
27 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
28 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
30 #define LEVEL_HEADER_UNUSED     13      /* unused level header bytes  */
31 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
33 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
34 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
35 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
36 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
37 #define LEVEL_CPART_CUS4_SIZE   ???     /* size of CUS4 chunk part    */
38 #define LEVEL_CPART_CUS4_UNUSED ???     /* unused CUS4 bytes / part   */
39 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
40 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
41
42 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + (x) * LEVEL_CPART_CUS3_SIZE)
43 #define LEVEL_CHUNK_CUS4_SIZE(x) (48 + 48 + (x) * 48)
44
45 /* file identifier strings */
46 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
47 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
48 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
49
50
51 /* ========================================================================= */
52 /* level file functions                                                      */
53 /* ========================================================================= */
54
55 void setElementChangePages(struct ElementInfo *ei, int change_pages)
56 {
57   int change_page_size = sizeof(struct ElementChangeInfo);
58
59   ei->num_change_pages = MAX(1, change_pages);
60
61   ei->change_page =
62     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
63
64   if (ei->current_change_page >= ei->num_change_pages)
65     ei->current_change_page = ei->num_change_pages - 1;
66
67   ei->change = &ei->change_page[ei->current_change_page];
68 }
69
70 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
71 {
72   int x, y;
73
74   change->can_change = FALSE;
75
76   change->events = CE_BITMASK_DEFAULT;
77   change->target_element = EL_EMPTY_SPACE;
78
79   change->delay_fixed = 0;
80   change->delay_random = 0;
81   change->delay_frames = -1;    /* later set to reliable default value */
82
83   change->trigger_element = EL_EMPTY_SPACE;
84
85   change->explode = FALSE;
86   change->use_content = FALSE;
87   change->only_complete = FALSE;
88   change->use_random_change = FALSE;
89   change->random = 0;
90   change->power = CP_NON_DESTRUCTIVE;
91
92   for(x=0; x<3; x++)
93     for(y=0; y<3; y++)
94       change->content[x][y] = EL_EMPTY_SPACE;
95
96   change->direct_action = 0;
97   change->other_action = 0;
98
99   change->pre_change_function = NULL;
100   change->change_function = NULL;
101   change->post_change_function = NULL;
102 }
103
104 static void setLevelInfoToDefaults(struct LevelInfo *level)
105 {
106   int i, j, x, y;
107
108   level->file_version = FILE_VERSION_ACTUAL;
109   level->game_version = GAME_VERSION_ACTUAL;
110
111   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
112   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
113   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
114
115   level->fieldx = STD_LEV_FIELDX;
116   level->fieldy = STD_LEV_FIELDY;
117
118   for(x=0; x<MAX_LEV_FIELDX; x++)
119     for(y=0; y<MAX_LEV_FIELDY; y++)
120       level->field[x][y] = EL_SAND;
121
122   level->time = 100;
123   level->gems_needed = 0;
124   level->amoeba_speed = 10;
125   level->time_magic_wall = 10;
126   level->time_wheel = 10;
127   level->time_light = 10;
128   level->time_timegate = 10;
129   level->amoeba_content = EL_DIAMOND;
130   level->double_speed = FALSE;
131   level->gravity = FALSE;
132   level->em_slippery_gems = FALSE;
133
134   level->use_custom_template = FALSE;
135
136   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
137     level->name[i] = '\0';
138   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
139     level->author[i] = '\0';
140
141   strcpy(level->name, NAMELESS_LEVEL_NAME);
142   strcpy(level->author, ANONYMOUS_NAME);
143
144   level->envelope[0] = '\0';
145   level->envelope_xsize = MAX_ENVELOPE_XSIZE;
146   level->envelope_ysize = MAX_ENVELOPE_YSIZE;
147
148   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
149     level->score[i] = 10;
150
151   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
152   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
153     for(x=0; x<3; x++)
154       for(y=0; y<3; y++)
155         level->yamyam_content[i][x][y] =
156           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
157
158   level->field[0][0] = EL_PLAYER_1;
159   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
160
161   for (i=0; i < MAX_NUM_ELEMENTS; i++)
162   {
163     setElementChangePages(&element_info[i], 1);
164     setElementChangeInfoToDefaults(element_info[i].change);
165   }
166
167   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
168   {
169     int element = EL_CUSTOM_START + i;
170
171     for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
172       element_info[element].description[j] = '\0';
173     if (element_info[element].custom_description != NULL)
174       strncpy(element_info[element].description,
175               element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
176     else
177       strcpy(element_info[element].description,
178              element_info[element].editor_description);
179
180     element_info[element].use_gfx_element = FALSE;
181     element_info[element].gfx_element = EL_EMPTY_SPACE;
182
183     element_info[element].collect_score = 10;           /* special default */
184     element_info[element].collect_count = 1;            /* special default */
185
186     element_info[element].push_delay_fixed = 2;         /* special default */
187     element_info[element].push_delay_random = 8;        /* special default */
188     element_info[element].move_delay_fixed = 0;
189     element_info[element].move_delay_random = 0;
190
191     element_info[element].move_pattern = MV_ALL_DIRECTIONS;
192     element_info[element].move_direction_initial = MV_NO_MOVING;
193     element_info[element].move_stepsize = TILEX / 8;
194
195     element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
196
197     for(x=0; x<3; x++)
198       for(y=0; y<3; y++)
199         element_info[element].content[x][y] = EL_EMPTY_SPACE;
200
201     element_info[element].access_type = 0;
202     element_info[element].access_layer = 0;
203     element_info[element].walk_to_action = 0;
204     element_info[element].smash_targets = 0;
205     element_info[element].deadliness = 0;
206     element_info[element].consistency = 0;
207
208     element_info[element].can_explode_by_fire = FALSE;
209     element_info[element].can_explode_smashed = FALSE;
210     element_info[element].can_explode_impact = FALSE;
211
212     element_info[element].current_change_page = 0;
213
214     /* start with no properties at all */
215     for (j=0; j < NUM_EP_BITFIELDS; j++)
216       Properties[element][j] = EP_BITMASK_DEFAULT;
217
218     element_info[element].modified_settings = FALSE;
219   }
220
221   BorderElement = EL_STEELWALL;
222
223   level->no_level_file = FALSE;
224
225   if (leveldir_current == NULL)         /* only when dumping level */
226     return;
227
228   /* try to determine better author name than 'anonymous' */
229   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
230   {
231     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
232     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
233   }
234   else
235   {
236     switch (LEVELCLASS(leveldir_current))
237     {
238       case LEVELCLASS_TUTORIAL:
239         strcpy(level->author, PROGRAM_AUTHOR_STRING);
240         break;
241
242       case LEVELCLASS_CONTRIBUTION:
243         strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
244         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
245         break;
246
247       case LEVELCLASS_USER:
248         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
249         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
250         break;
251
252       default:
253         /* keep default value */
254         break;
255     }
256   }
257 }
258
259 static void ActivateLevelTemplate()
260 {
261   /* Currently there is no special action needed to activate the template
262      data, because 'element_info' and 'Properties' overwrite the original
263      level data, while all other variables do not change. */
264 }
265
266 boolean LevelFileExists(int level_nr)
267 {
268   char *filename = getLevelFilename(level_nr);
269
270   return (access(filename, F_OK) == 0);
271 }
272
273 static int checkLevelElement(int element)
274 {
275   if (element >= NUM_FILE_ELEMENTS)
276   {
277     Error(ERR_WARN, "invalid level element %d", element);
278     element = EL_CHAR_QUESTION;
279   }
280   else if (element == EL_PLAYER_OBSOLETE)
281     element = EL_PLAYER_1;
282   else if (element == EL_KEY_OBSOLETE)
283     element = EL_KEY_1;
284
285   return element;
286 }
287
288 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
289 {
290   level->file_version = getFileVersion(file);
291   level->game_version = getFileVersion(file);
292
293   return chunk_size;
294 }
295
296 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
297 {
298   int i, x, y;
299
300   level->fieldx = getFile8Bit(file);
301   level->fieldy = getFile8Bit(file);
302
303   level->time           = getFile16BitBE(file);
304   level->gems_needed    = getFile16BitBE(file);
305
306   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
307     level->name[i] = getFile8Bit(file);
308   level->name[MAX_LEVEL_NAME_LEN] = 0;
309
310   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
311     level->score[i] = getFile8Bit(file);
312
313   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
314   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
315     for(y=0; y<3; y++)
316       for(x=0; x<3; x++)
317         level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
318
319   level->amoeba_speed           = getFile8Bit(file);
320   level->time_magic_wall        = getFile8Bit(file);
321   level->time_wheel             = getFile8Bit(file);
322   level->amoeba_content         = checkLevelElement(getFile8Bit(file));
323   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
324   level->gravity                = (getFile8Bit(file) == 1 ? TRUE : FALSE);
325   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
326   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
327
328   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
329
330   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
331
332   return chunk_size;
333 }
334
335 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
336 {
337   int i;
338
339   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
340     level->author[i] = getFile8Bit(file);
341   level->author[MAX_LEVEL_NAME_LEN] = 0;
342
343   return chunk_size;
344 }
345
346 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
347 {
348   int x, y;
349   int chunk_size_expected = level->fieldx * level->fieldy;
350
351   /* Note: "chunk_size" was wrong before version 2.0 when elements are
352      stored with 16-bit encoding (and should be twice as big then).
353      Even worse, playfield data was stored 16-bit when only yamyam content
354      contained 16-bit elements and vice versa. */
355
356   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
357     chunk_size_expected *= 2;
358
359   if (chunk_size_expected != chunk_size)
360   {
361     ReadUnusedBytesFromFile(file, chunk_size);
362     return chunk_size_expected;
363   }
364
365   for(y=0; y<level->fieldy; y++)
366     for(x=0; x<level->fieldx; x++)
367       level->field[x][y] =
368         checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
369                           getFile8Bit(file));
370   return chunk_size;
371 }
372
373 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
374 {
375   int i, x, y;
376   int header_size = 4;
377   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
378   int chunk_size_expected = header_size + content_size;
379
380   /* Note: "chunk_size" was wrong before version 2.0 when elements are
381      stored with 16-bit encoding (and should be twice as big then).
382      Even worse, playfield data was stored 16-bit when only yamyam content
383      contained 16-bit elements and vice versa. */
384
385   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
386     chunk_size_expected += content_size;
387
388   if (chunk_size_expected != chunk_size)
389   {
390     ReadUnusedBytesFromFile(file, chunk_size);
391     return chunk_size_expected;
392   }
393
394   getFile8Bit(file);
395   level->num_yamyam_contents = getFile8Bit(file);
396   getFile8Bit(file);
397   getFile8Bit(file);
398
399   /* correct invalid number of content fields -- should never happen */
400   if (level->num_yamyam_contents < 1 ||
401       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
402     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
403
404   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
405     for(y=0; y<3; y++)
406       for(x=0; x<3; x++)
407         level->yamyam_content[i][x][y] =
408           checkLevelElement(level->encoding_16bit_field ?
409                             getFile16BitBE(file) : getFile8Bit(file));
410   return chunk_size;
411 }
412
413 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
414 {
415   int i, x, y;
416   int element;
417   int num_contents, content_xsize, content_ysize;
418   int content_array[MAX_ELEMENT_CONTENTS][3][3];
419
420   element = checkLevelElement(getFile16BitBE(file));
421   num_contents = getFile8Bit(file);
422   content_xsize = getFile8Bit(file);
423   content_ysize = getFile8Bit(file);
424
425   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
426
427   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
428     for(y=0; y<3; y++)
429       for(x=0; x<3; x++)
430         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
431
432   /* correct invalid number of content fields -- should never happen */
433   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
434     num_contents = STD_ELEMENT_CONTENTS;
435
436   if (element == EL_YAMYAM)
437   {
438     level->num_yamyam_contents = num_contents;
439
440     for(i=0; i<num_contents; i++)
441       for(y=0; y<3; y++)
442         for(x=0; x<3; x++)
443           level->yamyam_content[i][x][y] = content_array[i][x][y];
444   }
445   else if (element == EL_BD_AMOEBA)
446   {
447     level->amoeba_content = content_array[0][0][0];
448   }
449   else
450   {
451     Error(ERR_WARN, "cannot load content for element '%d'", element);
452   }
453
454   return chunk_size;
455 }
456
457 static int LoadLevel_CNT3(FILE *file, int chunk_size, struct LevelInfo *level)
458 {
459   int i;
460   int element;
461   int envelope_len;
462   int chunk_size_expected;
463
464   element = checkLevelElement(getFile16BitBE(file));
465   envelope_len = getFile16BitBE(file);
466   level->envelope_xsize = getFile8Bit(file);
467   level->envelope_ysize = getFile8Bit(file);
468
469   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
470
471   chunk_size_expected = LEVEL_CHUNK_CNT3_HEADER + envelope_len;
472
473   if (chunk_size_expected != chunk_size)
474   {
475     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
476     return chunk_size_expected;
477   }
478
479   for(i=0; i < envelope_len; i++)
480     level->envelope[i] = getFile8Bit(file);
481
482   return chunk_size;
483 }
484
485 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
486 {
487   int num_changed_custom_elements = getFile16BitBE(file);
488   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
489   int i;
490
491   if (chunk_size_expected != chunk_size)
492   {
493     ReadUnusedBytesFromFile(file, chunk_size - 2);
494     return chunk_size_expected;
495   }
496
497   for (i=0; i < num_changed_custom_elements; i++)
498   {
499     int element = getFile16BitBE(file);
500     int properties = getFile32BitBE(file);
501
502     if (IS_CUSTOM_ELEMENT(element))
503       Properties[element][EP_BITFIELD_BASE] = properties;
504     else
505       Error(ERR_WARN, "invalid custom element number %d", element);
506   }
507
508   return chunk_size;
509 }
510
511 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
512 {
513   int num_changed_custom_elements = getFile16BitBE(file);
514   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
515   int i;
516
517   if (chunk_size_expected != chunk_size)
518   {
519     ReadUnusedBytesFromFile(file, chunk_size - 2);
520     return chunk_size_expected;
521   }
522
523   for (i=0; i < num_changed_custom_elements; i++)
524   {
525     int element = getFile16BitBE(file);
526     int custom_target_element = getFile16BitBE(file);
527
528     if (IS_CUSTOM_ELEMENT(element))
529       element_info[element].change->target_element = custom_target_element;
530     else
531       Error(ERR_WARN, "invalid custom element number %d", element);
532   }
533
534   return chunk_size;
535 }
536
537 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
538 {
539   int num_changed_custom_elements = getFile16BitBE(file);
540   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
541   int i, j, x, y;
542
543   if (chunk_size_expected != chunk_size)
544   {
545     ReadUnusedBytesFromFile(file, chunk_size - 2);
546     return chunk_size_expected;
547   }
548
549   for (i=0; i < num_changed_custom_elements; i++)
550   {
551     int element = getFile16BitBE(file);
552
553     if (!IS_CUSTOM_ELEMENT(element))
554     {
555       Error(ERR_WARN, "invalid custom element number %d", element);
556
557       element = EL_DEFAULT;     /* dummy element used for artwork config */
558     }
559
560     for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
561       element_info[element].description[j] = getFile8Bit(file);
562     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
563
564     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
565
566     /* some free bytes for future properties and padding */
567     ReadUnusedBytesFromFile(file, 7);
568
569     element_info[element].use_gfx_element = getFile8Bit(file);
570     element_info[element].gfx_element =
571       checkLevelElement(getFile16BitBE(file));
572
573     element_info[element].collect_score = getFile8Bit(file);
574     element_info[element].collect_count = getFile8Bit(file);
575
576     element_info[element].push_delay_fixed = getFile16BitBE(file);
577     element_info[element].push_delay_random = getFile16BitBE(file);
578     element_info[element].move_delay_fixed = getFile16BitBE(file);
579     element_info[element].move_delay_random = getFile16BitBE(file);
580
581     element_info[element].move_pattern = getFile16BitBE(file);
582     element_info[element].move_direction_initial = getFile8Bit(file);
583     element_info[element].move_stepsize = getFile8Bit(file);
584
585     for(y=0; y<3; y++)
586       for(x=0; x<3; x++)
587         element_info[element].content[x][y] =
588           checkLevelElement(getFile16BitBE(file));
589
590     element_info[element].change->events = getFile32BitBE(file);
591
592     element_info[element].change->target_element =
593       checkLevelElement(getFile16BitBE(file));
594
595     element_info[element].change->delay_fixed = getFile16BitBE(file);
596     element_info[element].change->delay_random = getFile16BitBE(file);
597     element_info[element].change->delay_frames = getFile16BitBE(file);
598
599     element_info[element].change->trigger_element =
600       checkLevelElement(getFile16BitBE(file));
601
602     element_info[element].change->explode = getFile8Bit(file);
603     element_info[element].change->use_content = getFile8Bit(file);
604     element_info[element].change->only_complete = getFile8Bit(file);
605     element_info[element].change->use_random_change = getFile8Bit(file);
606
607     element_info[element].change->random = getFile8Bit(file);
608     element_info[element].change->power = getFile8Bit(file);
609
610     for(y=0; y<3; y++)
611       for(x=0; x<3; x++)
612         element_info[element].change->content[x][y] =
613           checkLevelElement(getFile16BitBE(file));
614
615     element_info[element].slippery_type = getFile8Bit(file);
616
617     /* some free bytes for future properties and padding */
618     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
619
620     /* mark that this custom element has been modified */
621     element_info[element].modified_settings = TRUE;
622   }
623
624   return chunk_size;
625 }
626
627 static int LoadLevel_CUS4(FILE *file, int chunk_size, struct LevelInfo *level)
628 {
629   struct ElementInfo *ei;
630   int chunk_size_expected;
631   int element;
632   int i, x, y;
633
634   element = getFile16BitBE(file);
635
636   if (!IS_CUSTOM_ELEMENT(element))
637   {
638     Error(ERR_WARN, "invalid custom element number %d", element);
639
640     element = EL_DEFAULT;       /* dummy element used for artwork config */
641   }
642
643   ei = &element_info[element];
644
645   for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
646     ei->description[i] = getFile8Bit(file);
647   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
648
649   Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
650   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
651
652   ei->num_change_pages = getFile8Bit(file);
653
654   /* some free bytes for future base property values and padding */
655   ReadUnusedBytesFromFile(file, 5);
656
657   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
658   if (chunk_size_expected != chunk_size)
659   {
660     ReadUnusedBytesFromFile(file, chunk_size - 48);
661     return chunk_size_expected;
662   }
663
664   /* read custom property values */
665
666   ei->use_gfx_element = getFile8Bit(file);
667   ei->gfx_element = checkLevelElement(getFile16BitBE(file));
668
669   ei->collect_score = getFile8Bit(file);
670   ei->collect_count = getFile8Bit(file);
671
672   ei->push_delay_fixed = getFile16BitBE(file);
673   ei->push_delay_random = getFile16BitBE(file);
674   ei->move_delay_fixed = getFile16BitBE(file);
675   ei->move_delay_random = getFile16BitBE(file);
676
677   ei->move_pattern = getFile16BitBE(file);
678   ei->move_direction_initial = getFile8Bit(file);
679   ei->move_stepsize = getFile8Bit(file);
680
681   ei->slippery_type = getFile8Bit(file);
682
683   for(y=0; y<3; y++)
684     for(x=0; x<3; x++)
685       ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
686
687   /* some free bytes for future custom property values and padding */
688   ReadUnusedBytesFromFile(file, 12);
689
690   /* read change property values */
691
692   setElementChangePages(ei, ei->num_change_pages);
693
694   for (i=0; i < ei->num_change_pages; i++)
695   {
696     struct ElementChangeInfo *change = &ei->change_page[i];
697
698     /* always start with reliable default values */
699     setElementChangeInfoToDefaults(change);
700
701     change->events = getFile32BitBE(file);
702
703     change->target_element = checkLevelElement(getFile16BitBE(file));
704
705     change->delay_fixed = getFile16BitBE(file);
706     change->delay_random = getFile16BitBE(file);
707     change->delay_frames = getFile16BitBE(file);
708
709     change->trigger_element = checkLevelElement(getFile16BitBE(file));
710
711     change->explode = getFile8Bit(file);
712     change->use_content = getFile8Bit(file);
713     change->only_complete = getFile8Bit(file);
714     change->use_random_change = getFile8Bit(file);
715
716     change->random = getFile8Bit(file);
717     change->power = getFile8Bit(file);
718
719     for(y=0; y<3; y++)
720       for(x=0; x<3; x++)
721         change->content[x][y] = checkLevelElement(getFile16BitBE(file));
722
723     change->can_change = getFile8Bit(file);
724
725     /* some free bytes for future change property values and padding */
726     ReadUnusedBytesFromFile(file, 9);
727   }
728
729   /* mark this custom element as modified */
730   ei->modified_settings = TRUE;
731
732   return chunk_size;
733 }
734
735 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
736 {
737   char cookie[MAX_LINE_LEN];
738   char chunk_name[CHUNK_ID_LEN + 1];
739   int chunk_size;
740   FILE *file;
741
742   /* always start with reliable default values */
743   setLevelInfoToDefaults(level);
744
745   if (!(file = fopen(filename, MODE_READ)))
746   {
747     level->no_level_file = TRUE;
748
749     if (level != &level_template)
750       Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
751
752     return;
753   }
754
755   getFileChunkBE(file, chunk_name, NULL);
756   if (strcmp(chunk_name, "RND1") == 0)
757   {
758     getFile32BitBE(file);               /* not used */
759
760     getFileChunkBE(file, chunk_name, NULL);
761     if (strcmp(chunk_name, "CAVE") != 0)
762     {
763       Error(ERR_WARN, "unknown format of level file '%s'", filename);
764       fclose(file);
765       return;
766     }
767   }
768   else  /* check for pre-2.0 file format with cookie string */
769   {
770     strcpy(cookie, chunk_name);
771     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
772     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
773       cookie[strlen(cookie) - 1] = '\0';
774
775     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
776     {
777       Error(ERR_WARN, "unknown format of level file '%s'", filename);
778       fclose(file);
779       return;
780     }
781
782     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
783     {
784       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
785       fclose(file);
786       return;
787     }
788
789     /* pre-2.0 level files have no game version, so use file version here */
790     level->game_version = level->file_version;
791   }
792
793   if (level->file_version < FILE_VERSION_1_2)
794   {
795     /* level files from versions before 1.2.0 without chunk structure */
796     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
797     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
798   }
799   else
800   {
801     static struct
802     {
803       char *name;
804       int size;
805       int (*loader)(FILE *, int, struct LevelInfo *);
806     }
807     chunk_info[] =
808     {
809       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
810       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
811       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
812       { "BODY", -1,                     LoadLevel_BODY },
813       { "CONT", -1,                     LoadLevel_CONT },
814       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
815       { "CNT3", -1,                     LoadLevel_CNT3 },
816       { "CUS1", -1,                     LoadLevel_CUS1 },
817       { "CUS2", -1,                     LoadLevel_CUS2 },
818       { "CUS3", -1,                     LoadLevel_CUS3 },
819       { "CUS4", -1,                     LoadLevel_CUS4 },
820       {  NULL,  0,                      NULL }
821     };
822
823     while (getFileChunkBE(file, chunk_name, &chunk_size))
824     {
825       int i = 0;
826
827       while (chunk_info[i].name != NULL &&
828              strcmp(chunk_name, chunk_info[i].name) != 0)
829         i++;
830
831       if (chunk_info[i].name == NULL)
832       {
833         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
834               chunk_name, filename);
835         ReadUnusedBytesFromFile(file, chunk_size);
836       }
837       else if (chunk_info[i].size != -1 &&
838                chunk_info[i].size != chunk_size)
839       {
840         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
841               chunk_size, chunk_name, filename);
842         ReadUnusedBytesFromFile(file, chunk_size);
843       }
844       else
845       {
846         /* call function to load this level chunk */
847         int chunk_size_expected =
848           (chunk_info[i].loader)(file, chunk_size, level);
849
850         /* the size of some chunks cannot be checked before reading other
851            chunks first (like "HEAD" and "BODY") that contain some header
852            information, so check them here */
853         if (chunk_size_expected != chunk_size)
854         {
855           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
856                 chunk_size, chunk_name, filename);
857         }
858       }
859     }
860   }
861
862   fclose(file);
863 }
864
865 #if 1
866
867 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
868 {
869   if (leveldir_current == NULL)         /* only when dumping level */
870     return;
871
872   /* determine correct game engine version of current level */
873   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
874       IS_LEVELCLASS_USER(leveldir_current))
875   {
876 #if 0
877     printf("\n::: This level is private or contributed: '%s'\n", filename);
878 #endif
879
880     /* For user contributed and private levels, use the version of
881        the game engine the levels were created for.
882        Since 2.0.1, the game engine version is now directly stored
883        in the level file (chunk "VERS"), so there is no need anymore
884        to set the game version from the file version (except for old,
885        pre-2.0 levels, where the game version is still taken from the
886        file format version used to store the level -- see above). */
887
888     /* do some special adjustments to support older level versions */
889     if (level->file_version == FILE_VERSION_1_0)
890     {
891       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
892       Error(ERR_WARN, "using high speed movement for player");
893
894       /* player was faster than monsters in (pre-)1.0 levels */
895       level->double_speed = TRUE;
896     }
897
898     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
899     if (level->game_version == VERSION_IDENT(2,0,1))
900       level->em_slippery_gems = TRUE;
901   }
902   else
903   {
904 #if 0
905     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
906            leveldir_current->sort_priority, filename);
907 #endif
908
909     /* Always use the latest version of the game engine for all but
910        user contributed and private levels; this allows for actual
911        corrections in the game engine to take effect for existing,
912        converted levels (from "classic" or other existing games) to
913        make the game emulation more accurate, while (hopefully) not
914        breaking existing levels created from other players. */
915
916     level->game_version = GAME_VERSION_ACTUAL;
917
918     /* Set special EM style gems behaviour: EM style gems slip down from
919        normal, steel and growing wall. As this is a more fundamental change,
920        it seems better to set the default behaviour to "off" (as it is more
921        natural) and make it configurable in the level editor (as a property
922        of gem style elements). Already existing converted levels (neither
923        private nor contributed levels) are changed to the new behaviour. */
924
925     if (level->file_version < FILE_VERSION_2_0)
926       level->em_slippery_gems = TRUE;
927   }
928 }
929
930 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
931 {
932   int i, j;
933
934   /* map custom element change events that have changed in newer versions
935      (these following values were accidentally changed in version 3.0.1) */
936   if (level->game_version <= VERSION_IDENT(3,0,0))
937   {
938     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
939     {
940       int element = EL_CUSTOM_START + i;
941
942       /* order of checking and copying events to be mapped is important */
943       for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
944       {
945         if (HAS_CHANGE_EVENT(element, j - 2))
946         {
947           SET_CHANGE_EVENT(element, j - 2, FALSE);
948           SET_CHANGE_EVENT(element, j, TRUE);
949         }
950       }
951
952       /* order of checking and copying events to be mapped is important */
953       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
954       {
955         if (HAS_CHANGE_EVENT(element, j - 1))
956         {
957           SET_CHANGE_EVENT(element, j - 1, FALSE);
958           SET_CHANGE_EVENT(element, j, TRUE);
959         }
960       }
961     }
962   }
963
964   /* some custom element change events get mapped since version 3.0.3 */
965   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
966   {
967     int element = EL_CUSTOM_START + i;
968
969     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
970         HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
971     {
972       SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
973       SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
974
975       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
976     }
977   }
978
979   /* initialize "can_change" field for old levels with only one change page */
980   if (level->game_version <= VERSION_IDENT(3,0,2))
981   {
982     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
983     {
984       int element = EL_CUSTOM_START + i;
985
986       if (CAN_CHANGE(element))
987         element_info[element].change->can_change = TRUE;
988     }
989   }
990
991   /* initialize element properties for level editor etc. */
992   InitElementPropertiesEngine(level->game_version);
993 }
994
995 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
996 {
997   int x, y;
998
999   /* map elements that have changed in newer versions */
1000   for(y=0; y<level->fieldy; y++)
1001   {
1002     for(x=0; x<level->fieldx; x++)
1003     {
1004       int element = level->field[x][y];
1005
1006       if (level->game_version <= VERSION_IDENT(2,2,0))
1007       {
1008         /* map game font elements */
1009         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1010                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1011                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1012                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1013       }
1014
1015       if (level->game_version < VERSION_IDENT(3,0,0))
1016       {
1017         /* map Supaplex gravity tube elements */
1018         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1019                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1020                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1021                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1022                    element);
1023       }
1024
1025       level->field[x][y] = element;
1026     }
1027   }
1028
1029   /* copy elements to runtime playfield array */
1030   for(x=0; x<MAX_LEV_FIELDX; x++)
1031     for(y=0; y<MAX_LEV_FIELDY; y++)
1032       Feld[x][y] = level->field[x][y];
1033
1034   /* initialize level size variables for faster access */
1035   lev_fieldx = level->fieldx;
1036   lev_fieldy = level->fieldy;
1037
1038   /* determine border element for this level */
1039   SetBorderElement();
1040 }
1041
1042 #else
1043
1044 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1045 {
1046   int i, j, x, y;
1047
1048   if (leveldir_current == NULL)         /* only when dumping level */
1049     return;
1050
1051   /* determine correct game engine version of current level */
1052   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1053       IS_LEVELCLASS_USER(leveldir_current))
1054   {
1055 #if 0
1056     printf("\n::: This level is private or contributed: '%s'\n", filename);
1057 #endif
1058
1059     /* For user contributed and private levels, use the version of
1060        the game engine the levels were created for.
1061        Since 2.0.1, the game engine version is now directly stored
1062        in the level file (chunk "VERS"), so there is no need anymore
1063        to set the game version from the file version (except for old,
1064        pre-2.0 levels, where the game version is still taken from the
1065        file format version used to store the level -- see above). */
1066
1067     /* do some special adjustments to support older level versions */
1068     if (level->file_version == FILE_VERSION_1_0)
1069     {
1070       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1071       Error(ERR_WARN, "using high speed movement for player");
1072
1073       /* player was faster than monsters in (pre-)1.0 levels */
1074       level->double_speed = TRUE;
1075     }
1076
1077     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1078     if (level->game_version == VERSION_IDENT(2,0,1))
1079       level->em_slippery_gems = TRUE;
1080   }
1081   else
1082   {
1083 #if 0
1084     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1085            leveldir_current->sort_priority, filename);
1086 #endif
1087
1088     /* Always use the latest version of the game engine for all but
1089        user contributed and private levels; this allows for actual
1090        corrections in the game engine to take effect for existing,
1091        converted levels (from "classic" or other existing games) to
1092        make the game emulation more accurate, while (hopefully) not
1093        breaking existing levels created from other players. */
1094
1095     level->game_version = GAME_VERSION_ACTUAL;
1096
1097     /* Set special EM style gems behaviour: EM style gems slip down from
1098        normal, steel and growing wall. As this is a more fundamental change,
1099        it seems better to set the default behaviour to "off" (as it is more
1100        natural) and make it configurable in the level editor (as a property
1101        of gem style elements). Already existing converted levels (neither
1102        private nor contributed levels) are changed to the new behaviour. */
1103
1104     if (level->file_version < FILE_VERSION_2_0)
1105       level->em_slippery_gems = TRUE;
1106   }
1107
1108   /* map elements that have changed in newer versions */
1109   for(y=0; y<level->fieldy; y++)
1110   {
1111     for(x=0; x<level->fieldx; x++)
1112     {
1113       int element = level->field[x][y];
1114
1115       if (level->game_version <= VERSION_IDENT(2,2,0))
1116       {
1117         /* map game font elements */
1118         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1119                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1120                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1121                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1122       }
1123
1124       if (level->game_version < VERSION_IDENT(3,0,0))
1125       {
1126         /* map Supaplex gravity tube elements */
1127         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1128                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1129                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1130                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1131                    element);
1132       }
1133
1134       level->field[x][y] = element;
1135     }
1136   }
1137
1138   /* map custom element change events that have changed in newer versions
1139      (these following values have accidentally changed in version 3.0.1) */
1140   if (level->game_version <= VERSION_IDENT(3,0,0))
1141   {
1142     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1143     {
1144       int element = EL_CUSTOM_START + i;
1145
1146       /* order of checking events to be mapped is important */
1147       for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1148       {
1149         if (HAS_CHANGE_EVENT(element, j - 2))
1150         {
1151           SET_CHANGE_EVENT(element, j - 2, FALSE);
1152           SET_CHANGE_EVENT(element, j, TRUE);
1153         }
1154       }
1155
1156       /* order of checking events to be mapped is important */
1157       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1158       {
1159         if (HAS_CHANGE_EVENT(element, j - 1))
1160         {
1161           SET_CHANGE_EVENT(element, j - 1, FALSE);
1162           SET_CHANGE_EVENT(element, j, TRUE);
1163         }
1164       }
1165     }
1166   }
1167
1168   /* initialize "can_change" field for old levels with only one change page */
1169   if (level->game_version <= VERSION_IDENT(3,0,2))
1170   {
1171     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1172     {
1173       int element = EL_CUSTOM_START + i;
1174
1175       if (CAN_CHANGE(element))
1176         element_info[element].change->can_change = TRUE;
1177     }
1178   }
1179
1180   /* copy elements to runtime playfield array */
1181   for(x=0; x<MAX_LEV_FIELDX; x++)
1182     for(y=0; y<MAX_LEV_FIELDY; y++)
1183       Feld[x][y] = level->field[x][y];
1184
1185   /* initialize level size variables for faster access */
1186   lev_fieldx = level->fieldx;
1187   lev_fieldy = level->fieldy;
1188
1189   /* determine border element for this level */
1190   SetBorderElement();
1191
1192   /* initialize element properties for level editor etc. */
1193   InitElementPropertiesEngine(level->game_version);
1194 }
1195
1196 #endif
1197
1198 void LoadLevelTemplate(int level_nr)
1199 {
1200   char *filename = getLevelFilename(level_nr);
1201
1202   LoadLevelFromFilename(&level_template, filename);
1203
1204   LoadLevel_InitVersion(&level, filename);
1205   LoadLevel_InitElements(&level, filename);
1206
1207   ActivateLevelTemplate();
1208 }
1209
1210 void LoadLevel(int level_nr)
1211 {
1212   char *filename = getLevelFilename(level_nr);
1213
1214   LoadLevelFromFilename(&level, filename);
1215
1216   if (level.use_custom_template)
1217     LoadLevelTemplate(-1);
1218
1219 #if 1
1220   LoadLevel_InitVersion(&level, filename);
1221   LoadLevel_InitElements(&level, filename);
1222   LoadLevel_InitPlayfield(&level, filename);
1223 #else
1224   LoadLevel_InitLevel(&level, filename);
1225 #endif
1226 }
1227
1228 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1229 {
1230   putFileVersion(file, level->file_version);
1231   putFileVersion(file, level->game_version);
1232 }
1233
1234 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1235 {
1236   int i, x, y;
1237
1238   putFile8Bit(file, level->fieldx);
1239   putFile8Bit(file, level->fieldy);
1240
1241   putFile16BitBE(file, level->time);
1242   putFile16BitBE(file, level->gems_needed);
1243
1244   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1245     putFile8Bit(file, level->name[i]);
1246
1247   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1248     putFile8Bit(file, level->score[i]);
1249
1250   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1251     for(y=0; y<3; y++)
1252       for(x=0; x<3; x++)
1253         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1254                            level->yamyam_content[i][x][y]));
1255   putFile8Bit(file, level->amoeba_speed);
1256   putFile8Bit(file, level->time_magic_wall);
1257   putFile8Bit(file, level->time_wheel);
1258   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1259                      level->amoeba_content));
1260   putFile8Bit(file, (level->double_speed ? 1 : 0));
1261   putFile8Bit(file, (level->gravity ? 1 : 0));
1262   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1263   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1264
1265   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1266
1267   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1268 }
1269
1270 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1271 {
1272   int i;
1273
1274   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1275     putFile8Bit(file, level->author[i]);
1276 }
1277
1278 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1279 {
1280   int x, y;
1281
1282   for(y=0; y<level->fieldy; y++) 
1283     for(x=0; x<level->fieldx; x++) 
1284       if (level->encoding_16bit_field)
1285         putFile16BitBE(file, level->field[x][y]);
1286       else
1287         putFile8Bit(file, level->field[x][y]);
1288 }
1289
1290 #if 0
1291 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1292 {
1293   int i, x, y;
1294
1295   putFile8Bit(file, EL_YAMYAM);
1296   putFile8Bit(file, level->num_yamyam_contents);
1297   putFile8Bit(file, 0);
1298   putFile8Bit(file, 0);
1299
1300   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1301     for(y=0; y<3; y++)
1302       for(x=0; x<3; x++)
1303         if (level->encoding_16bit_field)
1304           putFile16BitBE(file, level->yamyam_content[i][x][y]);
1305         else
1306           putFile8Bit(file, level->yamyam_content[i][x][y]);
1307 }
1308 #endif
1309
1310 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1311 {
1312   int i, x, y;
1313   int num_contents, content_xsize, content_ysize;
1314   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1315
1316   if (element == EL_YAMYAM)
1317   {
1318     num_contents = level->num_yamyam_contents;
1319     content_xsize = 3;
1320     content_ysize = 3;
1321
1322     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1323       for(y=0; y<3; y++)
1324         for(x=0; x<3; x++)
1325           content_array[i][x][y] = level->yamyam_content[i][x][y];
1326   }
1327   else if (element == EL_BD_AMOEBA)
1328   {
1329     num_contents = 1;
1330     content_xsize = 1;
1331     content_ysize = 1;
1332
1333     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1334       for(y=0; y<3; y++)
1335         for(x=0; x<3; x++)
1336           content_array[i][x][y] = EL_EMPTY;
1337     content_array[0][0][0] = level->amoeba_content;
1338   }
1339   else
1340   {
1341     /* chunk header already written -- write empty chunk data */
1342     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1343
1344     Error(ERR_WARN, "cannot save content for element '%d'", element);
1345     return;
1346   }
1347
1348   putFile16BitBE(file, element);
1349   putFile8Bit(file, num_contents);
1350   putFile8Bit(file, content_xsize);
1351   putFile8Bit(file, content_ysize);
1352
1353   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1354
1355   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1356     for(y=0; y<3; y++)
1357       for(x=0; x<3; x++)
1358         putFile16BitBE(file, content_array[i][x][y]);
1359 }
1360
1361 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1362 {
1363   int i;
1364   int envelope_len = strlen(level->envelope) + 1;
1365
1366   putFile16BitBE(file, element);
1367   putFile16BitBE(file, envelope_len);
1368   putFile8Bit(file, level->envelope_xsize);
1369   putFile8Bit(file, level->envelope_ysize);
1370
1371   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1372
1373   for(i=0; i < envelope_len; i++)
1374     putFile8Bit(file, level->envelope[i]);
1375 }
1376
1377 #if 0
1378 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1379                            int num_changed_custom_elements)
1380 {
1381   int i, check = 0;
1382
1383   putFile16BitBE(file, num_changed_custom_elements);
1384
1385   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1386   {
1387     int element = EL_CUSTOM_START + i;
1388
1389     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1390     {
1391       if (check < num_changed_custom_elements)
1392       {
1393         putFile16BitBE(file, element);
1394         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1395       }
1396
1397       check++;
1398     }
1399   }
1400
1401   if (check != num_changed_custom_elements)     /* should not happen */
1402     Error(ERR_WARN, "inconsistent number of custom element properties");
1403 }
1404 #endif
1405
1406 #if 0
1407 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1408                            int num_changed_custom_elements)
1409 {
1410   int i, check = 0;
1411
1412   putFile16BitBE(file, num_changed_custom_elements);
1413
1414   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1415   {
1416     int element = EL_CUSTOM_START + i;
1417
1418     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1419     {
1420       if (check < num_changed_custom_elements)
1421       {
1422         putFile16BitBE(file, element);
1423         putFile16BitBE(file, element_info[element].change->target_element);
1424       }
1425
1426       check++;
1427     }
1428   }
1429
1430   if (check != num_changed_custom_elements)     /* should not happen */
1431     Error(ERR_WARN, "inconsistent number of custom target elements");
1432 }
1433 #endif
1434
1435 #if 0
1436 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1437                            int num_changed_custom_elements)
1438 {
1439   int i, j, x, y, check = 0;
1440
1441   putFile16BitBE(file, num_changed_custom_elements);
1442
1443   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1444   {
1445     int element = EL_CUSTOM_START + i;
1446
1447     if (element_info[element].modified_settings)
1448     {
1449       if (check < num_changed_custom_elements)
1450       {
1451         putFile16BitBE(file, element);
1452
1453         for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1454           putFile8Bit(file, element_info[element].description[j]);
1455
1456         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1457
1458         /* some free bytes for future properties and padding */
1459         WriteUnusedBytesToFile(file, 7);
1460
1461         putFile8Bit(file, element_info[element].use_gfx_element);
1462         putFile16BitBE(file, element_info[element].gfx_element);
1463
1464         putFile8Bit(file, element_info[element].collect_score);
1465         putFile8Bit(file, element_info[element].collect_count);
1466
1467         putFile16BitBE(file, element_info[element].push_delay_fixed);
1468         putFile16BitBE(file, element_info[element].push_delay_random);
1469         putFile16BitBE(file, element_info[element].move_delay_fixed);
1470         putFile16BitBE(file, element_info[element].move_delay_random);
1471
1472         putFile16BitBE(file, element_info[element].move_pattern);
1473         putFile8Bit(file, element_info[element].move_direction_initial);
1474         putFile8Bit(file, element_info[element].move_stepsize);
1475
1476         for(y=0; y<3; y++)
1477           for(x=0; x<3; x++)
1478             putFile16BitBE(file, element_info[element].content[x][y]);
1479
1480         putFile32BitBE(file, element_info[element].change->events);
1481
1482         putFile16BitBE(file, element_info[element].change->target_element);
1483
1484         putFile16BitBE(file, element_info[element].change->delay_fixed);
1485         putFile16BitBE(file, element_info[element].change->delay_random);
1486         putFile16BitBE(file, element_info[element].change->delay_frames);
1487
1488         putFile16BitBE(file, element_info[element].change->trigger_element);
1489
1490         putFile8Bit(file, element_info[element].change->explode);
1491         putFile8Bit(file, element_info[element].change->use_content);
1492         putFile8Bit(file, element_info[element].change->only_complete);
1493         putFile8Bit(file, element_info[element].change->use_random_change);
1494
1495         putFile8Bit(file, element_info[element].change->random);
1496         putFile8Bit(file, element_info[element].change->power);
1497
1498         for(y=0; y<3; y++)
1499           for(x=0; x<3; x++)
1500             putFile16BitBE(file, element_info[element].change->content[x][y]);
1501
1502         putFile8Bit(file, element_info[element].slippery_type);
1503
1504         /* some free bytes for future properties and padding */
1505         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1506       }
1507
1508       check++;
1509     }
1510   }
1511
1512   if (check != num_changed_custom_elements)     /* should not happen */
1513     Error(ERR_WARN, "inconsistent number of custom element properties");
1514 }
1515 #endif
1516
1517 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1518 {
1519   struct ElementInfo *ei = &element_info[element];
1520   int i, x, y;
1521
1522   putFile16BitBE(file, element);
1523
1524   for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1525     putFile8Bit(file, ei->description[i]);
1526
1527   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1528   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
1529
1530   putFile8Bit(file, ei->num_change_pages);
1531
1532   /* some free bytes for future base property values and padding */
1533   WriteUnusedBytesToFile(file, 5);
1534
1535   /* write custom property values */
1536
1537   putFile8Bit(file, ei->use_gfx_element);
1538   putFile16BitBE(file, ei->gfx_element);
1539
1540   putFile8Bit(file, ei->collect_score);
1541   putFile8Bit(file, ei->collect_count);
1542
1543   putFile16BitBE(file, ei->push_delay_fixed);
1544   putFile16BitBE(file, ei->push_delay_random);
1545   putFile16BitBE(file, ei->move_delay_fixed);
1546   putFile16BitBE(file, ei->move_delay_random);
1547
1548   putFile16BitBE(file, ei->move_pattern);
1549   putFile8Bit(file, ei->move_direction_initial);
1550   putFile8Bit(file, ei->move_stepsize);
1551
1552   putFile8Bit(file, ei->slippery_type);
1553
1554   for(y=0; y<3; y++)
1555     for(x=0; x<3; x++)
1556       putFile16BitBE(file, ei->content[x][y]);
1557
1558   /* some free bytes for future custom property values and padding */
1559   WriteUnusedBytesToFile(file, 12);
1560
1561   /* write change property values */
1562
1563   for (i=0; i < ei->num_change_pages; i++)
1564   {
1565     struct ElementChangeInfo *change = &ei->change_page[i];
1566
1567     putFile32BitBE(file, change->events);
1568
1569     putFile16BitBE(file, change->target_element);
1570
1571     putFile16BitBE(file, change->delay_fixed);
1572     putFile16BitBE(file, change->delay_random);
1573     putFile16BitBE(file, change->delay_frames);
1574
1575     putFile16BitBE(file, change->trigger_element);
1576
1577     putFile8Bit(file, change->explode);
1578     putFile8Bit(file, change->use_content);
1579     putFile8Bit(file, change->only_complete);
1580     putFile8Bit(file, change->use_random_change);
1581
1582     putFile8Bit(file, change->random);
1583     putFile8Bit(file, change->power);
1584
1585     for(y=0; y<3; y++)
1586       for(x=0; x<3; x++)
1587         putFile16BitBE(file, change->content[x][y]);
1588
1589     putFile8Bit(file, change->can_change);
1590
1591     /* some free bytes for future change property values and padding */
1592     WriteUnusedBytesToFile(file, 9);
1593   }
1594 }
1595
1596 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1597 {
1598   int body_chunk_size;
1599   int i, x, y;
1600   FILE *file;
1601
1602   if (!(file = fopen(filename, MODE_WRITE)))
1603   {
1604     Error(ERR_WARN, "cannot save level file '%s'", filename);
1605     return;
1606   }
1607
1608   level->file_version = FILE_VERSION_ACTUAL;
1609   level->game_version = GAME_VERSION_ACTUAL;
1610
1611   /* check level field for 16-bit elements */
1612   level->encoding_16bit_field = FALSE;
1613   for(y=0; y<level->fieldy; y++) 
1614     for(x=0; x<level->fieldx; x++) 
1615       if (level->field[x][y] > 255)
1616         level->encoding_16bit_field = TRUE;
1617
1618   /* check yamyam content for 16-bit elements */
1619   level->encoding_16bit_yamyam = FALSE;
1620   for(i=0; i<level->num_yamyam_contents; i++)
1621     for(y=0; y<3; y++)
1622       for(x=0; x<3; x++)
1623         if (level->yamyam_content[i][x][y] > 255)
1624           level->encoding_16bit_yamyam = TRUE;
1625
1626   /* check amoeba content for 16-bit elements */
1627   level->encoding_16bit_amoeba = FALSE;
1628   if (level->amoeba_content > 255)
1629     level->encoding_16bit_amoeba = TRUE;
1630
1631   /* calculate size of "BODY" chunk */
1632   body_chunk_size =
1633     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1634
1635   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1636   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1637
1638   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1639   SaveLevel_VERS(file, level);
1640
1641   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1642   SaveLevel_HEAD(file, level);
1643
1644   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1645   SaveLevel_AUTH(file, level);
1646
1647   putFileChunkBE(file, "BODY", body_chunk_size);
1648   SaveLevel_BODY(file, level);
1649
1650   if (level->encoding_16bit_yamyam ||
1651       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1652   {
1653     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1654     SaveLevel_CNT2(file, level, EL_YAMYAM);
1655   }
1656
1657   if (level->encoding_16bit_amoeba)
1658   {
1659     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1660     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1661   }
1662
1663   /* check for envelope content */
1664   if (strlen(level->envelope) > 0)
1665   {
1666     int envelope_len = strlen(level->envelope) + 1;
1667
1668     putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1669     SaveLevel_CNT3(file, level, EL_ENVELOPE);
1670   }
1671
1672   /* check for non-default custom elements (unless using template level) */
1673   if (!level->use_custom_template)
1674   {
1675     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1676     {
1677       int element = EL_CUSTOM_START + i;
1678
1679       if (element_info[element].modified_settings)
1680       {
1681         int num_change_pages = element_info[element].num_change_pages;
1682
1683         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1684         SaveLevel_CUS4(file, level, element);
1685       }
1686     }
1687   }
1688
1689   fclose(file);
1690
1691   SetFilePermissions(filename, PERMS_PRIVATE);
1692 }
1693
1694 void SaveLevel(int level_nr)
1695 {
1696   char *filename = getLevelFilename(level_nr);
1697
1698   SaveLevelFromFilename(&level, filename);
1699 }
1700
1701 void SaveLevelTemplate()
1702 {
1703   char *filename = getLevelFilename(-1);
1704
1705   SaveLevelFromFilename(&level, filename);
1706 }
1707
1708 void DumpLevel(struct LevelInfo *level)
1709 {
1710   printf_line("-", 79);
1711   printf("Level xxx (file version %08d, game version %08d)\n",
1712          level->file_version, level->game_version);
1713   printf_line("-", 79);
1714
1715   printf("Level Author: '%s'\n", level->author);
1716   printf("Level Title:  '%s'\n", level->name);
1717   printf("\n");
1718   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1719   printf("\n");
1720   printf("Level Time:  %d seconds\n", level->time);
1721   printf("Gems needed: %d\n", level->gems_needed);
1722   printf("\n");
1723   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1724   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
1725   printf("Time for Light:      %d seconds\n", level->time_light);
1726   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
1727   printf("\n");
1728   printf("Amoeba Speed: %d\n", level->amoeba_speed);
1729   printf("\n");
1730   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
1731   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
1732   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1733
1734   printf_line("-", 79);
1735 }
1736
1737
1738 /* ========================================================================= */
1739 /* tape file functions                                                       */
1740 /* ========================================================================= */
1741
1742 static void setTapeInfoToDefaults()
1743 {
1744   int i;
1745
1746   /* always start with reliable default values (empty tape) */
1747   TapeErase();
1748
1749   /* default values (also for pre-1.2 tapes) with only the first player */
1750   tape.player_participates[0] = TRUE;
1751   for(i=1; i<MAX_PLAYERS; i++)
1752     tape.player_participates[i] = FALSE;
1753
1754   /* at least one (default: the first) player participates in every tape */
1755   tape.num_participating_players = 1;
1756
1757   tape.level_nr = level_nr;
1758   tape.counter = 0;
1759   tape.changed = FALSE;
1760
1761   tape.recording = FALSE;
1762   tape.playing = FALSE;
1763   tape.pausing = FALSE;
1764 }
1765
1766 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1767 {
1768   tape->file_version = getFileVersion(file);
1769   tape->game_version = getFileVersion(file);
1770
1771   return chunk_size;
1772 }
1773
1774 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1775 {
1776   int i;
1777
1778   tape->random_seed = getFile32BitBE(file);
1779   tape->date        = getFile32BitBE(file);
1780   tape->length      = getFile32BitBE(file);
1781
1782   /* read header fields that are new since version 1.2 */
1783   if (tape->file_version >= FILE_VERSION_1_2)
1784   {
1785     byte store_participating_players = getFile8Bit(file);
1786     int engine_version;
1787
1788     /* since version 1.2, tapes store which players participate in the tape */
1789     tape->num_participating_players = 0;
1790     for(i=0; i<MAX_PLAYERS; i++)
1791     {
1792       tape->player_participates[i] = FALSE;
1793
1794       if (store_participating_players & (1 << i))
1795       {
1796         tape->player_participates[i] = TRUE;
1797         tape->num_participating_players++;
1798       }
1799     }
1800
1801     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1802
1803     engine_version = getFileVersion(file);
1804     if (engine_version > 0)
1805       tape->engine_version = engine_version;
1806     else
1807       tape->engine_version = tape->game_version;
1808   }
1809
1810   return chunk_size;
1811 }
1812
1813 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1814 {
1815   int level_identifier_size;
1816   int i;
1817
1818   level_identifier_size = getFile16BitBE(file);
1819
1820   tape->level_identifier =
1821     checked_realloc(tape->level_identifier, level_identifier_size);
1822
1823   for(i=0; i < level_identifier_size; i++)
1824     tape->level_identifier[i] = getFile8Bit(file);
1825
1826   tape->level_nr = getFile16BitBE(file);
1827
1828   chunk_size = 2 + level_identifier_size + 2;
1829
1830   return chunk_size;
1831 }
1832
1833 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1834 {
1835   int i, j;
1836   int chunk_size_expected =
1837     (tape->num_participating_players + 1) * tape->length;
1838
1839   if (chunk_size_expected != chunk_size)
1840   {
1841     ReadUnusedBytesFromFile(file, chunk_size);
1842     return chunk_size_expected;
1843   }
1844
1845   for(i=0; i<tape->length; i++)
1846   {
1847     if (i >= MAX_TAPELEN)
1848       break;
1849
1850     for(j=0; j<MAX_PLAYERS; j++)
1851     {
1852       tape->pos[i].action[j] = MV_NO_MOVING;
1853
1854       if (tape->player_participates[j])
1855         tape->pos[i].action[j] = getFile8Bit(file);
1856     }
1857
1858     tape->pos[i].delay = getFile8Bit(file);
1859
1860     if (tape->file_version == FILE_VERSION_1_0)
1861     {
1862       /* eliminate possible diagonal moves in old tapes */
1863       /* this is only for backward compatibility */
1864
1865       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1866       byte action = tape->pos[i].action[0];
1867       int k, num_moves = 0;
1868
1869       for (k=0; k<4; k++)
1870       {
1871         if (action & joy_dir[k])
1872         {
1873           tape->pos[i + num_moves].action[0] = joy_dir[k];
1874           if (num_moves > 0)
1875             tape->pos[i + num_moves].delay = 0;
1876           num_moves++;
1877         }
1878       }
1879
1880       if (num_moves > 1)
1881       {
1882         num_moves--;
1883         i += num_moves;
1884         tape->length += num_moves;
1885       }
1886     }
1887     else if (tape->file_version < FILE_VERSION_2_0)
1888     {
1889       /* convert pre-2.0 tapes to new tape format */
1890
1891       if (tape->pos[i].delay > 1)
1892       {
1893         /* action part */
1894         tape->pos[i + 1] = tape->pos[i];
1895         tape->pos[i + 1].delay = 1;
1896
1897         /* delay part */
1898         for(j=0; j<MAX_PLAYERS; j++)
1899           tape->pos[i].action[j] = MV_NO_MOVING;
1900         tape->pos[i].delay--;
1901
1902         i++;
1903         tape->length++;
1904       }
1905     }
1906
1907     if (feof(file))
1908       break;
1909   }
1910
1911   if (i != tape->length)
1912     chunk_size = (tape->num_participating_players + 1) * i;
1913
1914   return chunk_size;
1915 }
1916
1917 void LoadTapeFromFilename(char *filename)
1918 {
1919   char cookie[MAX_LINE_LEN];
1920   char chunk_name[CHUNK_ID_LEN + 1];
1921   FILE *file;
1922   int chunk_size;
1923
1924   /* always start with reliable default values */
1925   setTapeInfoToDefaults();
1926
1927   if (!(file = fopen(filename, MODE_READ)))
1928     return;
1929
1930   getFileChunkBE(file, chunk_name, NULL);
1931   if (strcmp(chunk_name, "RND1") == 0)
1932   {
1933     getFile32BitBE(file);               /* not used */
1934
1935     getFileChunkBE(file, chunk_name, NULL);
1936     if (strcmp(chunk_name, "TAPE") != 0)
1937     {
1938       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1939       fclose(file);
1940       return;
1941     }
1942   }
1943   else  /* check for pre-2.0 file format with cookie string */
1944   {
1945     strcpy(cookie, chunk_name);
1946     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1947     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1948       cookie[strlen(cookie) - 1] = '\0';
1949
1950     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1951     {
1952       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1953       fclose(file);
1954       return;
1955     }
1956
1957     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1958     {
1959       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1960       fclose(file);
1961       return;
1962     }
1963
1964     /* pre-2.0 tape files have no game version, so use file version here */
1965     tape.game_version = tape.file_version;
1966   }
1967
1968   if (tape.file_version < FILE_VERSION_1_2)
1969   {
1970     /* tape files from versions before 1.2.0 without chunk structure */
1971     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1972     LoadTape_BODY(file, 2 * tape.length,  &tape);
1973   }
1974   else
1975   {
1976     static struct
1977     {
1978       char *name;
1979       int size;
1980       int (*loader)(FILE *, int, struct TapeInfo *);
1981     }
1982     chunk_info[] =
1983     {
1984       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1985       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1986       { "INFO", -1,                     LoadTape_INFO },
1987       { "BODY", -1,                     LoadTape_BODY },
1988       {  NULL,  0,                      NULL }
1989     };
1990
1991     while (getFileChunkBE(file, chunk_name, &chunk_size))
1992     {
1993       int i = 0;
1994
1995       while (chunk_info[i].name != NULL &&
1996              strcmp(chunk_name, chunk_info[i].name) != 0)
1997         i++;
1998
1999       if (chunk_info[i].name == NULL)
2000       {
2001         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
2002               chunk_name, filename);
2003         ReadUnusedBytesFromFile(file, chunk_size);
2004       }
2005       else if (chunk_info[i].size != -1 &&
2006                chunk_info[i].size != chunk_size)
2007       {
2008         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2009               chunk_size, chunk_name, filename);
2010         ReadUnusedBytesFromFile(file, chunk_size);
2011       }
2012       else
2013       {
2014         /* call function to load this tape chunk */
2015         int chunk_size_expected =
2016           (chunk_info[i].loader)(file, chunk_size, &tape);
2017
2018         /* the size of some chunks cannot be checked before reading other
2019            chunks first (like "HEAD" and "BODY") that contain some header
2020            information, so check them here */
2021         if (chunk_size_expected != chunk_size)
2022         {
2023           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2024                 chunk_size, chunk_name, filename);
2025         }
2026       }
2027     }
2028   }
2029
2030   fclose(file);
2031
2032   tape.length_seconds = GetTapeLength();
2033
2034 #if 0
2035   printf("tape game version: %d\n", tape.game_version);
2036   printf("tape engine version: %d\n", tape.engine_version);
2037 #endif
2038 }
2039
2040 void LoadTape(int level_nr)
2041 {
2042   char *filename = getTapeFilename(level_nr);
2043
2044   LoadTapeFromFilename(filename);
2045 }
2046
2047 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2048 {
2049   putFileVersion(file, tape->file_version);
2050   putFileVersion(file, tape->game_version);
2051 }
2052
2053 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2054 {
2055   int i;
2056   byte store_participating_players = 0;
2057
2058   /* set bits for participating players for compact storage */
2059   for(i=0; i<MAX_PLAYERS; i++)
2060     if (tape->player_participates[i])
2061       store_participating_players |= (1 << i);
2062
2063   putFile32BitBE(file, tape->random_seed);
2064   putFile32BitBE(file, tape->date);
2065   putFile32BitBE(file, tape->length);
2066
2067   putFile8Bit(file, store_participating_players);
2068
2069   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2070   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2071
2072   putFileVersion(file, tape->engine_version);
2073 }
2074
2075 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2076 {
2077   int level_identifier_size = strlen(tape->level_identifier) + 1;
2078   int i;
2079
2080   putFile16BitBE(file, level_identifier_size);
2081
2082   for(i=0; i < level_identifier_size; i++)
2083     putFile8Bit(file, tape->level_identifier[i]);
2084
2085   putFile16BitBE(file, tape->level_nr);
2086 }
2087
2088 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2089 {
2090   int i, j;
2091
2092   for(i=0; i<tape->length; i++)
2093   {
2094     for(j=0; j<MAX_PLAYERS; j++)
2095       if (tape->player_participates[j])
2096         putFile8Bit(file, tape->pos[i].action[j]);
2097
2098     putFile8Bit(file, tape->pos[i].delay);
2099   }
2100 }
2101
2102 void SaveTape(int level_nr)
2103 {
2104   char *filename = getTapeFilename(level_nr);
2105   FILE *file;
2106   boolean new_tape = TRUE;
2107   int num_participating_players = 0;
2108   int info_chunk_size;
2109   int body_chunk_size;
2110   int i;
2111
2112   InitTapeDirectory(leveldir_current->filename);
2113
2114   /* if a tape still exists, ask to overwrite it */
2115   if (access(filename, F_OK) == 0)
2116   {
2117     new_tape = FALSE;
2118     if (!Request("Replace old tape ?", REQ_ASK))
2119       return;
2120   }
2121
2122   if (!(file = fopen(filename, MODE_WRITE)))
2123   {
2124     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2125     return;
2126   }
2127
2128   tape.file_version = FILE_VERSION_ACTUAL;
2129   tape.game_version = GAME_VERSION_ACTUAL;
2130
2131   /* count number of participating players  */
2132   for(i=0; i<MAX_PLAYERS; i++)
2133     if (tape.player_participates[i])
2134       num_participating_players++;
2135
2136   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2137   body_chunk_size = (num_participating_players + 1) * tape.length;
2138
2139   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2140   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2141
2142   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2143   SaveTape_VERS(file, &tape);
2144
2145   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2146   SaveTape_HEAD(file, &tape);
2147
2148   putFileChunkBE(file, "INFO", info_chunk_size);
2149   SaveTape_INFO(file, &tape);
2150
2151   putFileChunkBE(file, "BODY", body_chunk_size);
2152   SaveTape_BODY(file, &tape);
2153
2154   fclose(file);
2155
2156   SetFilePermissions(filename, PERMS_PRIVATE);
2157
2158   tape.changed = FALSE;
2159
2160   if (new_tape)
2161     Request("tape saved !", REQ_CONFIRM);
2162 }
2163
2164 void DumpTape(struct TapeInfo *tape)
2165 {
2166   int i, j;
2167
2168   if (TAPE_IS_EMPTY(*tape))
2169   {
2170     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2171     return;
2172   }
2173
2174   printf_line("-", 79);
2175   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2176          tape->level_nr, tape->file_version, tape->game_version);
2177   printf("Level series identifier: '%s'\n", tape->level_identifier);
2178   printf_line("-", 79);
2179
2180   for(i=0; i<tape->length; i++)
2181   {
2182     if (i >= MAX_TAPELEN)
2183       break;
2184
2185     printf("%03d: ", i);
2186
2187     for(j=0; j<MAX_PLAYERS; j++)
2188     {
2189       if (tape->player_participates[j])
2190       {
2191         int action = tape->pos[i].action[j];
2192
2193         printf("%d:%02x ", j, action);
2194         printf("[%c%c%c%c|%c%c] - ",
2195                (action & JOY_LEFT ? '<' : ' '),
2196                (action & JOY_RIGHT ? '>' : ' '),
2197                (action & JOY_UP ? '^' : ' '),
2198                (action & JOY_DOWN ? 'v' : ' '),
2199                (action & JOY_BUTTON_1 ? '1' : ' '),
2200                (action & JOY_BUTTON_2 ? '2' : ' '));
2201       }
2202     }
2203
2204     printf("(%03d)\n", tape->pos[i].delay);
2205   }
2206
2207   printf_line("-", 79);
2208 }
2209
2210
2211 /* ========================================================================= */
2212 /* score file functions                                                      */
2213 /* ========================================================================= */
2214
2215 void LoadScore(int level_nr)
2216 {
2217   int i;
2218   char *filename = getScoreFilename(level_nr);
2219   char cookie[MAX_LINE_LEN];
2220   char line[MAX_LINE_LEN];
2221   char *line_ptr;
2222   FILE *file;
2223
2224   /* always start with reliable default values */
2225   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2226   {
2227     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2228     highscore[i].Score = 0;
2229   }
2230
2231   if (!(file = fopen(filename, MODE_READ)))
2232     return;
2233
2234   /* check file identifier */
2235   fgets(cookie, MAX_LINE_LEN, file);
2236   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2237     cookie[strlen(cookie) - 1] = '\0';
2238
2239   if (!checkCookieString(cookie, SCORE_COOKIE))
2240   {
2241     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2242     fclose(file);
2243     return;
2244   }
2245
2246   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2247   {
2248     fscanf(file, "%d", &highscore[i].Score);
2249     fgets(line, MAX_LINE_LEN, file);
2250
2251     if (line[strlen(line) - 1] == '\n')
2252       line[strlen(line) - 1] = '\0';
2253
2254     for (line_ptr = line; *line_ptr; line_ptr++)
2255     {
2256       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2257       {
2258         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2259         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2260         break;
2261       }
2262     }
2263   }
2264
2265   fclose(file);
2266 }
2267
2268 void SaveScore(int level_nr)
2269 {
2270   int i;
2271   char *filename = getScoreFilename(level_nr);
2272   FILE *file;
2273
2274   InitScoreDirectory(leveldir_current->filename);
2275
2276   if (!(file = fopen(filename, MODE_WRITE)))
2277   {
2278     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2279     return;
2280   }
2281
2282   fprintf(file, "%s\n\n", SCORE_COOKIE);
2283
2284   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2285     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2286
2287   fclose(file);
2288
2289   SetFilePermissions(filename, PERMS_PUBLIC);
2290 }
2291
2292
2293 /* ========================================================================= */
2294 /* setup file functions                                                      */
2295 /* ========================================================================= */
2296
2297 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2298
2299 /* global setup */
2300 #define SETUP_TOKEN_PLAYER_NAME                 0
2301 #define SETUP_TOKEN_SOUND                       1
2302 #define SETUP_TOKEN_SOUND_LOOPS                 2
2303 #define SETUP_TOKEN_SOUND_MUSIC                 3
2304 #define SETUP_TOKEN_SOUND_SIMPLE                4
2305 #define SETUP_TOKEN_TOONS                       5
2306 #define SETUP_TOKEN_SCROLL_DELAY                6
2307 #define SETUP_TOKEN_SOFT_SCROLLING              7
2308 #define SETUP_TOKEN_FADING                      8
2309 #define SETUP_TOKEN_AUTORECORD                  9
2310 #define SETUP_TOKEN_QUICK_DOORS                 10
2311 #define SETUP_TOKEN_TEAM_MODE                   11
2312 #define SETUP_TOKEN_HANDICAP                    12
2313 #define SETUP_TOKEN_TIME_LIMIT                  13
2314 #define SETUP_TOKEN_FULLSCREEN                  14
2315 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2316 #define SETUP_TOKEN_GRAPHICS_SET                16
2317 #define SETUP_TOKEN_SOUNDS_SET                  17
2318 #define SETUP_TOKEN_MUSIC_SET                   18
2319 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2320 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2321 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2322
2323 #define NUM_GLOBAL_SETUP_TOKENS                 22
2324
2325 /* editor setup */
2326 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2327 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2328 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2329 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2330 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2331 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2332 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2333 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2334 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2335 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2336 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2337
2338 #define NUM_EDITOR_SETUP_TOKENS                 11
2339
2340 /* shortcut setup */
2341 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2342 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2343 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2344
2345 #define NUM_SHORTCUT_SETUP_TOKENS               3
2346
2347 /* player setup */
2348 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2349 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2350 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2351 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2352 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2353 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2354 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2355 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2356 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2357 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
2358 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2359 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2360 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2361 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2362 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2363 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
2364
2365 #define NUM_PLAYER_SETUP_TOKENS                 16
2366
2367 /* system setup */
2368 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2369 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2370
2371 #define NUM_SYSTEM_SETUP_TOKENS                 2
2372
2373 /* options setup */
2374 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2375
2376 #define NUM_OPTIONS_SETUP_TOKENS                1
2377
2378
2379 static struct SetupInfo si;
2380 static struct SetupEditorInfo sei;
2381 static struct SetupShortcutInfo ssi;
2382 static struct SetupInputInfo sii;
2383 static struct SetupSystemInfo syi;
2384 static struct OptionInfo soi;
2385
2386 static struct TokenInfo global_setup_tokens[] =
2387 {
2388   { TYPE_STRING, &si.player_name,       "player_name"                   },
2389   { TYPE_SWITCH, &si.sound,             "sound"                         },
2390   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2391   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2392   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2393   { TYPE_SWITCH, &si.toons,             "toons"                         },
2394   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2395   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2396   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2397   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2398   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2399   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2400   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2401   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2402   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2403   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2404   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2405   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2406   { TYPE_STRING, &si.music_set,         "music_set"                     },
2407   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2408   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2409   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2410 };
2411
2412 static struct TokenInfo editor_setup_tokens[] =
2413 {
2414   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2415   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2416   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2417   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2418   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2419   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2420   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2421   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2422   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2423   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2424   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2425 };
2426
2427 static struct TokenInfo shortcut_setup_tokens[] =
2428 {
2429   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2430   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2431   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2432 };
2433
2434 static struct TokenInfo player_setup_tokens[] =
2435 {
2436   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2437   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2438   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2439   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2440   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2441   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2442   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2443   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2444   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2445   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2446   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2447   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2448   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2449   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2450   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2451   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
2452 };
2453
2454 static struct TokenInfo system_setup_tokens[] =
2455 {
2456   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2457   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2458 };
2459
2460 static struct TokenInfo options_setup_tokens[] =
2461 {
2462   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2463 };
2464
2465 static char *get_corrected_login_name(char *login_name)
2466 {
2467   /* needed because player name must be a fixed length string */
2468   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2469
2470   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2471   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2472
2473   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2474     if (strchr(login_name_new, ' '))
2475       *strchr(login_name_new, ' ') = '\0';
2476
2477   return login_name_new;
2478 }
2479
2480 static void setSetupInfoToDefaults(struct SetupInfo *si)
2481 {
2482   int i;
2483
2484   si->player_name = get_corrected_login_name(getLoginName());
2485
2486   si->sound = TRUE;
2487   si->sound_loops = TRUE;
2488   si->sound_music = TRUE;
2489   si->sound_simple = TRUE;
2490   si->toons = TRUE;
2491   si->double_buffering = TRUE;
2492   si->direct_draw = !si->double_buffering;
2493   si->scroll_delay = TRUE;
2494   si->soft_scrolling = TRUE;
2495   si->fading = FALSE;
2496   si->autorecord = TRUE;
2497   si->quick_doors = FALSE;
2498   si->team_mode = FALSE;
2499   si->handicap = TRUE;
2500   si->time_limit = TRUE;
2501   si->fullscreen = FALSE;
2502   si->ask_on_escape = TRUE;
2503
2504   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2505   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2506   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2507   si->override_level_graphics = FALSE;
2508   si->override_level_sounds = FALSE;
2509   si->override_level_music = FALSE;
2510
2511   si->editor.el_boulderdash = TRUE;
2512   si->editor.el_emerald_mine = TRUE;
2513   si->editor.el_more = TRUE;
2514   si->editor.el_sokoban = TRUE;
2515   si->editor.el_supaplex = TRUE;
2516   si->editor.el_diamond_caves = TRUE;
2517   si->editor.el_dx_boulderdash = TRUE;
2518   si->editor.el_chars = TRUE;
2519   si->editor.el_custom = TRUE;
2520   si->editor.el_custom_more = FALSE;
2521
2522   si->editor.el_headlines = TRUE;
2523
2524   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2525   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2526   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2527
2528   for (i=0; i<MAX_PLAYERS; i++)
2529   {
2530     si->input[i].use_joystick = FALSE;
2531     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2532     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2533     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2534     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2535     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2536     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2537     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2538     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2539     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2540     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2541     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2542     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2543     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2544     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2545     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2546   }
2547
2548   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2549   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2550
2551   si->options.verbose = FALSE;
2552 }
2553
2554 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2555 {
2556   int i, pnr;
2557
2558   if (!setup_file_hash)
2559     return;
2560
2561   /* global setup */
2562   si = setup;
2563   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2564     setSetupInfo(global_setup_tokens, i,
2565                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2566   setup = si;
2567
2568   /* editor setup */
2569   sei = setup.editor;
2570   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2571     setSetupInfo(editor_setup_tokens, i,
2572                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2573   setup.editor = sei;
2574
2575   /* shortcut setup */
2576   ssi = setup.shortcut;
2577   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2578     setSetupInfo(shortcut_setup_tokens, i,
2579                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2580   setup.shortcut = ssi;
2581
2582   /* player setup */
2583   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2584   {
2585     char prefix[30];
2586
2587     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2588
2589     sii = setup.input[pnr];
2590     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2591     {
2592       char full_token[100];
2593
2594       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2595       setSetupInfo(player_setup_tokens, i,
2596                    getHashEntry(setup_file_hash, full_token));
2597     }
2598     setup.input[pnr] = sii;
2599   }
2600
2601   /* system setup */
2602   syi = setup.system;
2603   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2604     setSetupInfo(system_setup_tokens, i,
2605                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2606   setup.system = syi;
2607
2608   /* options setup */
2609   soi = setup.options;
2610   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2611     setSetupInfo(options_setup_tokens, i,
2612                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2613   setup.options = soi;
2614 }
2615
2616 void LoadSetup()
2617 {
2618   char *filename = getSetupFilename();
2619   SetupFileHash *setup_file_hash = NULL;
2620
2621   /* always start with reliable default values */
2622   setSetupInfoToDefaults(&setup);
2623
2624   setup_file_hash = loadSetupFileHash(filename);
2625
2626   if (setup_file_hash)
2627   {
2628     char *player_name_new;
2629
2630     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2631     decodeSetupFileHash(setup_file_hash);
2632
2633     setup.direct_draw = !setup.double_buffering;
2634
2635     freeSetupFileHash(setup_file_hash);
2636
2637     /* needed to work around problems with fixed length strings */
2638     player_name_new = get_corrected_login_name(setup.player_name);
2639     free(setup.player_name);
2640     setup.player_name = player_name_new;
2641   }
2642   else
2643     Error(ERR_WARN, "using default setup values");
2644 }
2645
2646 void SaveSetup()
2647 {
2648   char *filename = getSetupFilename();
2649   FILE *file;
2650   int i, pnr;
2651
2652   InitUserDataDirectory();
2653
2654   if (!(file = fopen(filename, MODE_WRITE)))
2655   {
2656     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2657     return;
2658   }
2659
2660   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2661                                                getCookie("SETUP")));
2662   fprintf(file, "\n");
2663
2664   /* global setup */
2665   si = setup;
2666   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2667   {
2668     /* just to make things nicer :) */
2669     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2670         i == SETUP_TOKEN_GRAPHICS_SET)
2671       fprintf(file, "\n");
2672
2673     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2674   }
2675
2676   /* editor setup */
2677   sei = setup.editor;
2678   fprintf(file, "\n");
2679   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2680     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2681
2682   /* shortcut setup */
2683   ssi = setup.shortcut;
2684   fprintf(file, "\n");
2685   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2686     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2687
2688   /* player setup */
2689   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2690   {
2691     char prefix[30];
2692
2693     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2694     fprintf(file, "\n");
2695
2696     sii = setup.input[pnr];
2697     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2698       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2699   }
2700
2701   /* system setup */
2702   syi = setup.system;
2703   fprintf(file, "\n");
2704   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2705     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2706
2707   /* options setup */
2708   soi = setup.options;
2709   fprintf(file, "\n");
2710   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2711     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2712
2713   fclose(file);
2714
2715   SetFilePermissions(filename, PERMS_PRIVATE);
2716 }
2717
2718 void LoadCustomElementDescriptions()
2719 {
2720   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2721   SetupFileHash *setup_file_hash;
2722   int i;
2723
2724   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2725   {
2726     if (element_info[i].custom_description != NULL)
2727     {
2728       free(element_info[i].custom_description);
2729       element_info[i].custom_description = NULL;
2730     }
2731   }
2732
2733   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2734     return;
2735
2736   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2737   {
2738     char *token = getStringCat2(element_info[i].token_name, ".name");
2739     char *value = getHashEntry(setup_file_hash, token);
2740
2741     if (value != NULL)
2742       element_info[i].custom_description = getStringCopy(value);
2743
2744     free(token);
2745   }
2746
2747   freeSetupFileHash(setup_file_hash);
2748 }
2749
2750 void LoadSpecialMenuDesignSettings()
2751 {
2752   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2753   SetupFileHash *setup_file_hash;
2754   int i, j;
2755
2756   /* always start with reliable default values from default config */
2757   for (i=0; image_config_vars[i].token != NULL; i++)
2758     for (j=0; image_config[j].token != NULL; j++)
2759       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2760         *image_config_vars[i].value =
2761           get_integer_from_string(image_config[j].value);
2762
2763   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2764     return;
2765
2766   /* special case: initialize with default values that may be overwritten */
2767   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2768   {
2769     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2770     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2771     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2772
2773     if (value_x != NULL)
2774       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2775     if (value_y != NULL)
2776       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2777     if (list_size != NULL)
2778       menu.list_size[i] = get_integer_from_string(list_size);
2779   }
2780
2781   /* read (and overwrite with) values that may be specified in config file */
2782   for (i=0; image_config_vars[i].token != NULL; i++)
2783   {
2784     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2785
2786     if (value != NULL)
2787       *image_config_vars[i].value = get_integer_from_string(value);
2788   }
2789
2790   freeSetupFileHash(setup_file_hash);
2791 }