ac3f0b4b86d92fafaa49f29987038a9ad8c3812f
[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     change->events = getFile32BitBE(file);
699
700     change->target_element = checkLevelElement(getFile16BitBE(file));
701
702     change->delay_fixed = getFile16BitBE(file);
703     change->delay_random = getFile16BitBE(file);
704     change->delay_frames = getFile16BitBE(file);
705
706     change->trigger_element = checkLevelElement(getFile16BitBE(file));
707
708     change->explode = getFile8Bit(file);
709     change->use_content = getFile8Bit(file);
710     change->only_complete = getFile8Bit(file);
711     change->use_random_change = getFile8Bit(file);
712
713     change->random = getFile8Bit(file);
714     change->power = getFile8Bit(file);
715
716     for(y=0; y<3; y++)
717       for(x=0; x<3; x++)
718         change->content[x][y] = checkLevelElement(getFile16BitBE(file));
719
720     change->can_change = getFile8Bit(file);
721
722     /* some free bytes for future change property values and padding */
723     ReadUnusedBytesFromFile(file, 9);
724   }
725
726   /* mark this custom element as modified */
727   ei->modified_settings = TRUE;
728
729   return chunk_size;
730 }
731
732 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
733 {
734   char cookie[MAX_LINE_LEN];
735   char chunk_name[CHUNK_ID_LEN + 1];
736   int chunk_size;
737   FILE *file;
738
739   /* always start with reliable default values */
740   setLevelInfoToDefaults(level);
741
742   if (!(file = fopen(filename, MODE_READ)))
743   {
744     level->no_level_file = TRUE;
745
746     if (level != &level_template)
747       Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
748
749     return;
750   }
751
752   getFileChunkBE(file, chunk_name, NULL);
753   if (strcmp(chunk_name, "RND1") == 0)
754   {
755     getFile32BitBE(file);               /* not used */
756
757     getFileChunkBE(file, chunk_name, NULL);
758     if (strcmp(chunk_name, "CAVE") != 0)
759     {
760       Error(ERR_WARN, "unknown format of level file '%s'", filename);
761       fclose(file);
762       return;
763     }
764   }
765   else  /* check for pre-2.0 file format with cookie string */
766   {
767     strcpy(cookie, chunk_name);
768     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
769     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
770       cookie[strlen(cookie) - 1] = '\0';
771
772     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
773     {
774       Error(ERR_WARN, "unknown format of level file '%s'", filename);
775       fclose(file);
776       return;
777     }
778
779     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
780     {
781       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
782       fclose(file);
783       return;
784     }
785
786     /* pre-2.0 level files have no game version, so use file version here */
787     level->game_version = level->file_version;
788   }
789
790   if (level->file_version < FILE_VERSION_1_2)
791   {
792     /* level files from versions before 1.2.0 without chunk structure */
793     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
794     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
795   }
796   else
797   {
798     static struct
799     {
800       char *name;
801       int size;
802       int (*loader)(FILE *, int, struct LevelInfo *);
803     }
804     chunk_info[] =
805     {
806       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
807       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
808       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
809       { "BODY", -1,                     LoadLevel_BODY },
810       { "CONT", -1,                     LoadLevel_CONT },
811       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
812       { "CNT3", -1,                     LoadLevel_CNT3 },
813       { "CUS1", -1,                     LoadLevel_CUS1 },
814       { "CUS2", -1,                     LoadLevel_CUS2 },
815       { "CUS3", -1,                     LoadLevel_CUS3 },
816       { "CUS4", -1,                     LoadLevel_CUS4 },
817       {  NULL,  0,                      NULL }
818     };
819
820     while (getFileChunkBE(file, chunk_name, &chunk_size))
821     {
822       int i = 0;
823
824       while (chunk_info[i].name != NULL &&
825              strcmp(chunk_name, chunk_info[i].name) != 0)
826         i++;
827
828       if (chunk_info[i].name == NULL)
829       {
830         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
831               chunk_name, filename);
832         ReadUnusedBytesFromFile(file, chunk_size);
833       }
834       else if (chunk_info[i].size != -1 &&
835                chunk_info[i].size != chunk_size)
836       {
837         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
838               chunk_size, chunk_name, filename);
839         ReadUnusedBytesFromFile(file, chunk_size);
840       }
841       else
842       {
843         /* call function to load this level chunk */
844         int chunk_size_expected =
845           (chunk_info[i].loader)(file, chunk_size, level);
846
847         /* the size of some chunks cannot be checked before reading other
848            chunks first (like "HEAD" and "BODY") that contain some header
849            information, so check them here */
850         if (chunk_size_expected != chunk_size)
851         {
852           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
853                 chunk_size, chunk_name, filename);
854         }
855       }
856     }
857   }
858
859   fclose(file);
860 }
861
862 #if 1
863
864 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
865 {
866   if (leveldir_current == NULL)         /* only when dumping level */
867     return;
868
869   /* determine correct game engine version of current level */
870   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
871       IS_LEVELCLASS_USER(leveldir_current))
872   {
873 #if 0
874     printf("\n::: This level is private or contributed: '%s'\n", filename);
875 #endif
876
877     /* For user contributed and private levels, use the version of
878        the game engine the levels were created for.
879        Since 2.0.1, the game engine version is now directly stored
880        in the level file (chunk "VERS"), so there is no need anymore
881        to set the game version from the file version (except for old,
882        pre-2.0 levels, where the game version is still taken from the
883        file format version used to store the level -- see above). */
884
885     /* do some special adjustments to support older level versions */
886     if (level->file_version == FILE_VERSION_1_0)
887     {
888       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
889       Error(ERR_WARN, "using high speed movement for player");
890
891       /* player was faster than monsters in (pre-)1.0 levels */
892       level->double_speed = TRUE;
893     }
894
895     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
896     if (level->game_version == VERSION_IDENT(2,0,1))
897       level->em_slippery_gems = TRUE;
898   }
899   else
900   {
901 #if 0
902     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
903            leveldir_current->sort_priority, filename);
904 #endif
905
906     /* Always use the latest version of the game engine for all but
907        user contributed and private levels; this allows for actual
908        corrections in the game engine to take effect for existing,
909        converted levels (from "classic" or other existing games) to
910        make the game emulation more accurate, while (hopefully) not
911        breaking existing levels created from other players. */
912
913     level->game_version = GAME_VERSION_ACTUAL;
914
915     /* Set special EM style gems behaviour: EM style gems slip down from
916        normal, steel and growing wall. As this is a more fundamental change,
917        it seems better to set the default behaviour to "off" (as it is more
918        natural) and make it configurable in the level editor (as a property
919        of gem style elements). Already existing converted levels (neither
920        private nor contributed levels) are changed to the new behaviour. */
921
922     if (level->file_version < FILE_VERSION_2_0)
923       level->em_slippery_gems = TRUE;
924   }
925 }
926
927 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
928 {
929   int i, j;
930
931   /* map custom element change events that have changed in newer versions
932      (these following values were accidentally changed in version 3.0.1) */
933   if (level->game_version <= VERSION_IDENT(3,0,0))
934   {
935     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
936     {
937       int element = EL_CUSTOM_START + i;
938
939       /* order of checking and copying events to be mapped is important */
940       for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
941       {
942         if (HAS_CHANGE_EVENT(element, j - 2))
943         {
944           SET_CHANGE_EVENT(element, j - 2, FALSE);
945           SET_CHANGE_EVENT(element, j, TRUE);
946         }
947       }
948
949       /* order of checking and copying events to be mapped is important */
950       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
951       {
952         if (HAS_CHANGE_EVENT(element, j - 1))
953         {
954           SET_CHANGE_EVENT(element, j - 1, FALSE);
955           SET_CHANGE_EVENT(element, j, TRUE);
956         }
957       }
958     }
959   }
960
961   /* some custom element change events get mapped since version 3.0.3 */
962   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
963   {
964     int element = EL_CUSTOM_START + i;
965
966     if (HAS_CHANGE_EVENT(element, CE_BY_PLAYER) ||
967         HAS_CHANGE_EVENT(element, CE_BY_COLLISION))
968     {
969       SET_CHANGE_EVENT(element, CE_BY_PLAYER, FALSE);
970       SET_CHANGE_EVENT(element, CE_BY_COLLISION, FALSE);
971
972       SET_CHANGE_EVENT(element, CE_BY_DIRECT_ACTION, TRUE);
973     }
974   }
975
976   /* initialize "can_change" field for old levels with only one change page */
977   if (level->game_version <= VERSION_IDENT(3,0,2))
978   {
979     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
980     {
981       int element = EL_CUSTOM_START + i;
982
983       if (CAN_CHANGE(element))
984         element_info[element].change->can_change = TRUE;
985     }
986   }
987
988   /* initialize element properties for level editor etc. */
989   InitElementPropertiesEngine(level->game_version);
990 }
991
992 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
993 {
994   int x, y;
995
996   /* map elements that have changed in newer versions */
997   for(y=0; y<level->fieldy; y++)
998   {
999     for(x=0; x<level->fieldx; x++)
1000     {
1001       int element = level->field[x][y];
1002
1003       if (level->game_version <= VERSION_IDENT(2,2,0))
1004       {
1005         /* map game font elements */
1006         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1007                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1008                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1009                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1010       }
1011
1012       if (level->game_version < VERSION_IDENT(3,0,0))
1013       {
1014         /* map Supaplex gravity tube elements */
1015         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1016                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1017                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1018                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1019                    element);
1020       }
1021
1022       level->field[x][y] = element;
1023     }
1024   }
1025
1026   /* copy elements to runtime playfield array */
1027   for(x=0; x<MAX_LEV_FIELDX; x++)
1028     for(y=0; y<MAX_LEV_FIELDY; y++)
1029       Feld[x][y] = level->field[x][y];
1030
1031   /* initialize level size variables for faster access */
1032   lev_fieldx = level->fieldx;
1033   lev_fieldy = level->fieldy;
1034
1035   /* determine border element for this level */
1036   SetBorderElement();
1037 }
1038
1039 #else
1040
1041 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
1042 {
1043   int i, j, x, y;
1044
1045   if (leveldir_current == NULL)         /* only when dumping level */
1046     return;
1047
1048   /* determine correct game engine version of current level */
1049   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
1050       IS_LEVELCLASS_USER(leveldir_current))
1051   {
1052 #if 0
1053     printf("\n::: This level is private or contributed: '%s'\n", filename);
1054 #endif
1055
1056     /* For user contributed and private levels, use the version of
1057        the game engine the levels were created for.
1058        Since 2.0.1, the game engine version is now directly stored
1059        in the level file (chunk "VERS"), so there is no need anymore
1060        to set the game version from the file version (except for old,
1061        pre-2.0 levels, where the game version is still taken from the
1062        file format version used to store the level -- see above). */
1063
1064     /* do some special adjustments to support older level versions */
1065     if (level->file_version == FILE_VERSION_1_0)
1066     {
1067       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
1068       Error(ERR_WARN, "using high speed movement for player");
1069
1070       /* player was faster than monsters in (pre-)1.0 levels */
1071       level->double_speed = TRUE;
1072     }
1073
1074     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
1075     if (level->game_version == VERSION_IDENT(2,0,1))
1076       level->em_slippery_gems = TRUE;
1077   }
1078   else
1079   {
1080 #if 0
1081     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
1082            leveldir_current->sort_priority, filename);
1083 #endif
1084
1085     /* Always use the latest version of the game engine for all but
1086        user contributed and private levels; this allows for actual
1087        corrections in the game engine to take effect for existing,
1088        converted levels (from "classic" or other existing games) to
1089        make the game emulation more accurate, while (hopefully) not
1090        breaking existing levels created from other players. */
1091
1092     level->game_version = GAME_VERSION_ACTUAL;
1093
1094     /* Set special EM style gems behaviour: EM style gems slip down from
1095        normal, steel and growing wall. As this is a more fundamental change,
1096        it seems better to set the default behaviour to "off" (as it is more
1097        natural) and make it configurable in the level editor (as a property
1098        of gem style elements). Already existing converted levels (neither
1099        private nor contributed levels) are changed to the new behaviour. */
1100
1101     if (level->file_version < FILE_VERSION_2_0)
1102       level->em_slippery_gems = TRUE;
1103   }
1104
1105   /* map elements that have changed in newer versions */
1106   for(y=0; y<level->fieldy; y++)
1107   {
1108     for(x=0; x<level->fieldx; x++)
1109     {
1110       int element = level->field[x][y];
1111
1112       if (level->game_version <= VERSION_IDENT(2,2,0))
1113       {
1114         /* map game font elements */
1115         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
1116                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
1117                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
1118                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
1119       }
1120
1121       if (level->game_version < VERSION_IDENT(3,0,0))
1122       {
1123         /* map Supaplex gravity tube elements */
1124         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
1125                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
1126                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
1127                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
1128                    element);
1129       }
1130
1131       level->field[x][y] = element;
1132     }
1133   }
1134
1135   /* map custom element change events that have changed in newer versions
1136      (these following values have accidentally changed in version 3.0.1) */
1137   if (level->game_version <= VERSION_IDENT(3,0,0))
1138   {
1139     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1140     {
1141       int element = EL_CUSTOM_START + i;
1142
1143       /* order of checking events to be mapped is important */
1144       for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
1145       {
1146         if (HAS_CHANGE_EVENT(element, j - 2))
1147         {
1148           SET_CHANGE_EVENT(element, j - 2, FALSE);
1149           SET_CHANGE_EVENT(element, j, TRUE);
1150         }
1151       }
1152
1153       /* order of checking events to be mapped is important */
1154       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
1155       {
1156         if (HAS_CHANGE_EVENT(element, j - 1))
1157         {
1158           SET_CHANGE_EVENT(element, j - 1, FALSE);
1159           SET_CHANGE_EVENT(element, j, TRUE);
1160         }
1161       }
1162     }
1163   }
1164
1165   /* initialize "can_change" field for old levels with only one change page */
1166   if (level->game_version <= VERSION_IDENT(3,0,2))
1167   {
1168     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1169     {
1170       int element = EL_CUSTOM_START + i;
1171
1172       if (CAN_CHANGE(element))
1173         element_info[element].change->can_change = TRUE;
1174     }
1175   }
1176
1177   /* copy elements to runtime playfield array */
1178   for(x=0; x<MAX_LEV_FIELDX; x++)
1179     for(y=0; y<MAX_LEV_FIELDY; y++)
1180       Feld[x][y] = level->field[x][y];
1181
1182   /* initialize level size variables for faster access */
1183   lev_fieldx = level->fieldx;
1184   lev_fieldy = level->fieldy;
1185
1186   /* determine border element for this level */
1187   SetBorderElement();
1188
1189   /* initialize element properties for level editor etc. */
1190   InitElementPropertiesEngine(level->game_version);
1191 }
1192
1193 #endif
1194
1195 void LoadLevelTemplate(int level_nr)
1196 {
1197   char *filename = getLevelFilename(level_nr);
1198
1199   LoadLevelFromFilename(&level_template, filename);
1200
1201   LoadLevel_InitVersion(&level, filename);
1202   LoadLevel_InitElements(&level, filename);
1203
1204   ActivateLevelTemplate();
1205 }
1206
1207 void LoadLevel(int level_nr)
1208 {
1209   char *filename = getLevelFilename(level_nr);
1210
1211   LoadLevelFromFilename(&level, filename);
1212
1213   if (level.use_custom_template)
1214     LoadLevelTemplate(-1);
1215
1216 #if 1
1217   LoadLevel_InitVersion(&level, filename);
1218   LoadLevel_InitElements(&level, filename);
1219   LoadLevel_InitPlayfield(&level, filename);
1220 #else
1221   LoadLevel_InitLevel(&level, filename);
1222 #endif
1223 }
1224
1225 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
1226 {
1227   putFileVersion(file, level->file_version);
1228   putFileVersion(file, level->game_version);
1229 }
1230
1231 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
1232 {
1233   int i, x, y;
1234
1235   putFile8Bit(file, level->fieldx);
1236   putFile8Bit(file, level->fieldy);
1237
1238   putFile16BitBE(file, level->time);
1239   putFile16BitBE(file, level->gems_needed);
1240
1241   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
1242     putFile8Bit(file, level->name[i]);
1243
1244   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
1245     putFile8Bit(file, level->score[i]);
1246
1247   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
1248     for(y=0; y<3; y++)
1249       for(x=0; x<3; x++)
1250         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
1251                            level->yamyam_content[i][x][y]));
1252   putFile8Bit(file, level->amoeba_speed);
1253   putFile8Bit(file, level->time_magic_wall);
1254   putFile8Bit(file, level->time_wheel);
1255   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
1256                      level->amoeba_content));
1257   putFile8Bit(file, (level->double_speed ? 1 : 0));
1258   putFile8Bit(file, (level->gravity ? 1 : 0));
1259   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
1260   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
1261
1262   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
1263
1264   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
1265 }
1266
1267 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
1268 {
1269   int i;
1270
1271   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
1272     putFile8Bit(file, level->author[i]);
1273 }
1274
1275 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
1276 {
1277   int x, y;
1278
1279   for(y=0; y<level->fieldy; y++) 
1280     for(x=0; x<level->fieldx; x++) 
1281       if (level->encoding_16bit_field)
1282         putFile16BitBE(file, level->field[x][y]);
1283       else
1284         putFile8Bit(file, level->field[x][y]);
1285 }
1286
1287 #if 0
1288 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
1289 {
1290   int i, x, y;
1291
1292   putFile8Bit(file, EL_YAMYAM);
1293   putFile8Bit(file, level->num_yamyam_contents);
1294   putFile8Bit(file, 0);
1295   putFile8Bit(file, 0);
1296
1297   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1298     for(y=0; y<3; y++)
1299       for(x=0; x<3; x++)
1300         if (level->encoding_16bit_field)
1301           putFile16BitBE(file, level->yamyam_content[i][x][y]);
1302         else
1303           putFile8Bit(file, level->yamyam_content[i][x][y]);
1304 }
1305 #endif
1306
1307 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
1308 {
1309   int i, x, y;
1310   int num_contents, content_xsize, content_ysize;
1311   int content_array[MAX_ELEMENT_CONTENTS][3][3];
1312
1313   if (element == EL_YAMYAM)
1314   {
1315     num_contents = level->num_yamyam_contents;
1316     content_xsize = 3;
1317     content_ysize = 3;
1318
1319     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1320       for(y=0; y<3; y++)
1321         for(x=0; x<3; x++)
1322           content_array[i][x][y] = level->yamyam_content[i][x][y];
1323   }
1324   else if (element == EL_BD_AMOEBA)
1325   {
1326     num_contents = 1;
1327     content_xsize = 1;
1328     content_ysize = 1;
1329
1330     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1331       for(y=0; y<3; y++)
1332         for(x=0; x<3; x++)
1333           content_array[i][x][y] = EL_EMPTY;
1334     content_array[0][0][0] = level->amoeba_content;
1335   }
1336   else
1337   {
1338     /* chunk header already written -- write empty chunk data */
1339     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
1340
1341     Error(ERR_WARN, "cannot save content for element '%d'", element);
1342     return;
1343   }
1344
1345   putFile16BitBE(file, element);
1346   putFile8Bit(file, num_contents);
1347   putFile8Bit(file, content_xsize);
1348   putFile8Bit(file, content_ysize);
1349
1350   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1351
1352   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1353     for(y=0; y<3; y++)
1354       for(x=0; x<3; x++)
1355         putFile16BitBE(file, content_array[i][x][y]);
1356 }
1357
1358 static void SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
1359 {
1360   int i;
1361   int envelope_len = strlen(level->envelope) + 1;
1362
1363   putFile16BitBE(file, element);
1364   putFile16BitBE(file, envelope_len);
1365   putFile8Bit(file, level->envelope_xsize);
1366   putFile8Bit(file, level->envelope_ysize);
1367
1368   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
1369
1370   for(i=0; i < envelope_len; i++)
1371     putFile8Bit(file, level->envelope[i]);
1372 }
1373
1374 #if 0
1375 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1376                            int num_changed_custom_elements)
1377 {
1378   int i, check = 0;
1379
1380   putFile16BitBE(file, num_changed_custom_elements);
1381
1382   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1383   {
1384     int element = EL_CUSTOM_START + i;
1385
1386     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1387     {
1388       if (check < num_changed_custom_elements)
1389       {
1390         putFile16BitBE(file, element);
1391         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1392       }
1393
1394       check++;
1395     }
1396   }
1397
1398   if (check != num_changed_custom_elements)     /* should not happen */
1399     Error(ERR_WARN, "inconsistent number of custom element properties");
1400 }
1401 #endif
1402
1403 #if 0
1404 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1405                            int num_changed_custom_elements)
1406 {
1407   int i, check = 0;
1408
1409   putFile16BitBE(file, num_changed_custom_elements);
1410
1411   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1412   {
1413     int element = EL_CUSTOM_START + i;
1414
1415     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1416     {
1417       if (check < num_changed_custom_elements)
1418       {
1419         putFile16BitBE(file, element);
1420         putFile16BitBE(file, element_info[element].change->target_element);
1421       }
1422
1423       check++;
1424     }
1425   }
1426
1427   if (check != num_changed_custom_elements)     /* should not happen */
1428     Error(ERR_WARN, "inconsistent number of custom target elements");
1429 }
1430 #endif
1431
1432 #if 0
1433 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1434                            int num_changed_custom_elements)
1435 {
1436   int i, j, x, y, check = 0;
1437
1438   putFile16BitBE(file, num_changed_custom_elements);
1439
1440   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1441   {
1442     int element = EL_CUSTOM_START + i;
1443
1444     if (element_info[element].modified_settings)
1445     {
1446       if (check < num_changed_custom_elements)
1447       {
1448         putFile16BitBE(file, element);
1449
1450         for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1451           putFile8Bit(file, element_info[element].description[j]);
1452
1453         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1454
1455         /* some free bytes for future properties and padding */
1456         WriteUnusedBytesToFile(file, 7);
1457
1458         putFile8Bit(file, element_info[element].use_gfx_element);
1459         putFile16BitBE(file, element_info[element].gfx_element);
1460
1461         putFile8Bit(file, element_info[element].collect_score);
1462         putFile8Bit(file, element_info[element].collect_count);
1463
1464         putFile16BitBE(file, element_info[element].push_delay_fixed);
1465         putFile16BitBE(file, element_info[element].push_delay_random);
1466         putFile16BitBE(file, element_info[element].move_delay_fixed);
1467         putFile16BitBE(file, element_info[element].move_delay_random);
1468
1469         putFile16BitBE(file, element_info[element].move_pattern);
1470         putFile8Bit(file, element_info[element].move_direction_initial);
1471         putFile8Bit(file, element_info[element].move_stepsize);
1472
1473         for(y=0; y<3; y++)
1474           for(x=0; x<3; x++)
1475             putFile16BitBE(file, element_info[element].content[x][y]);
1476
1477         putFile32BitBE(file, element_info[element].change->events);
1478
1479         putFile16BitBE(file, element_info[element].change->target_element);
1480
1481         putFile16BitBE(file, element_info[element].change->delay_fixed);
1482         putFile16BitBE(file, element_info[element].change->delay_random);
1483         putFile16BitBE(file, element_info[element].change->delay_frames);
1484
1485         putFile16BitBE(file, element_info[element].change->trigger_element);
1486
1487         putFile8Bit(file, element_info[element].change->explode);
1488         putFile8Bit(file, element_info[element].change->use_content);
1489         putFile8Bit(file, element_info[element].change->only_complete);
1490         putFile8Bit(file, element_info[element].change->use_random_change);
1491
1492         putFile8Bit(file, element_info[element].change->random);
1493         putFile8Bit(file, element_info[element].change->power);
1494
1495         for(y=0; y<3; y++)
1496           for(x=0; x<3; x++)
1497             putFile16BitBE(file, element_info[element].change->content[x][y]);
1498
1499         putFile8Bit(file, element_info[element].slippery_type);
1500
1501         /* some free bytes for future properties and padding */
1502         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1503       }
1504
1505       check++;
1506     }
1507   }
1508
1509   if (check != num_changed_custom_elements)     /* should not happen */
1510     Error(ERR_WARN, "inconsistent number of custom element properties");
1511 }
1512 #endif
1513
1514 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
1515 {
1516   struct ElementInfo *ei = &element_info[element];
1517   int i, x, y;
1518
1519   putFile16BitBE(file, element);
1520
1521   for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
1522     putFile8Bit(file, ei->description[i]);
1523
1524   putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1525   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
1526
1527   putFile8Bit(file, ei->num_change_pages);
1528
1529   /* some free bytes for future base property values and padding */
1530   WriteUnusedBytesToFile(file, 5);
1531
1532   /* write custom property values */
1533
1534   putFile8Bit(file, ei->use_gfx_element);
1535   putFile16BitBE(file, ei->gfx_element);
1536
1537   putFile8Bit(file, ei->collect_score);
1538   putFile8Bit(file, ei->collect_count);
1539
1540   putFile16BitBE(file, ei->push_delay_fixed);
1541   putFile16BitBE(file, ei->push_delay_random);
1542   putFile16BitBE(file, ei->move_delay_fixed);
1543   putFile16BitBE(file, ei->move_delay_random);
1544
1545   putFile16BitBE(file, ei->move_pattern);
1546   putFile8Bit(file, ei->move_direction_initial);
1547   putFile8Bit(file, ei->move_stepsize);
1548
1549   putFile8Bit(file, ei->slippery_type);
1550
1551   for(y=0; y<3; y++)
1552     for(x=0; x<3; x++)
1553       putFile16BitBE(file, ei->content[x][y]);
1554
1555   /* some free bytes for future custom property values and padding */
1556   WriteUnusedBytesToFile(file, 12);
1557
1558   /* write change property values */
1559
1560   for (i=0; i < ei->num_change_pages; i++)
1561   {
1562     struct ElementChangeInfo *change = &ei->change_page[i];
1563
1564     putFile32BitBE(file, change->events);
1565
1566     putFile16BitBE(file, change->target_element);
1567
1568     putFile16BitBE(file, change->delay_fixed);
1569     putFile16BitBE(file, change->delay_random);
1570     putFile16BitBE(file, change->delay_frames);
1571
1572     putFile16BitBE(file, change->trigger_element);
1573
1574     putFile8Bit(file, change->explode);
1575     putFile8Bit(file, change->use_content);
1576     putFile8Bit(file, change->only_complete);
1577     putFile8Bit(file, change->use_random_change);
1578
1579     putFile8Bit(file, change->random);
1580     putFile8Bit(file, change->power);
1581
1582     for(y=0; y<3; y++)
1583       for(x=0; x<3; x++)
1584         putFile16BitBE(file, change->content[x][y]);
1585
1586     putFile8Bit(file, change->can_change);
1587
1588     /* some free bytes for future change property values and padding */
1589     WriteUnusedBytesToFile(file, 9);
1590   }
1591 }
1592
1593 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1594 {
1595   int body_chunk_size;
1596   int i, x, y;
1597   FILE *file;
1598
1599   if (!(file = fopen(filename, MODE_WRITE)))
1600   {
1601     Error(ERR_WARN, "cannot save level file '%s'", filename);
1602     return;
1603   }
1604
1605   level->file_version = FILE_VERSION_ACTUAL;
1606   level->game_version = GAME_VERSION_ACTUAL;
1607
1608   /* check level field for 16-bit elements */
1609   level->encoding_16bit_field = FALSE;
1610   for(y=0; y<level->fieldy; y++) 
1611     for(x=0; x<level->fieldx; x++) 
1612       if (level->field[x][y] > 255)
1613         level->encoding_16bit_field = TRUE;
1614
1615   /* check yamyam content for 16-bit elements */
1616   level->encoding_16bit_yamyam = FALSE;
1617   for(i=0; i<level->num_yamyam_contents; i++)
1618     for(y=0; y<3; y++)
1619       for(x=0; x<3; x++)
1620         if (level->yamyam_content[i][x][y] > 255)
1621           level->encoding_16bit_yamyam = TRUE;
1622
1623   /* check amoeba content for 16-bit elements */
1624   level->encoding_16bit_amoeba = FALSE;
1625   if (level->amoeba_content > 255)
1626     level->encoding_16bit_amoeba = TRUE;
1627
1628   /* calculate size of "BODY" chunk */
1629   body_chunk_size =
1630     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1631
1632   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1633   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1634
1635   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1636   SaveLevel_VERS(file, level);
1637
1638   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1639   SaveLevel_HEAD(file, level);
1640
1641   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1642   SaveLevel_AUTH(file, level);
1643
1644   putFileChunkBE(file, "BODY", body_chunk_size);
1645   SaveLevel_BODY(file, level);
1646
1647   if (level->encoding_16bit_yamyam ||
1648       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1649   {
1650     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1651     SaveLevel_CNT2(file, level, EL_YAMYAM);
1652   }
1653
1654   if (level->encoding_16bit_amoeba)
1655   {
1656     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1657     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1658   }
1659
1660   /* check for envelope content */
1661   if (strlen(level->envelope) > 0)
1662   {
1663     int envelope_len = strlen(level->envelope) + 1;
1664
1665     putFileChunkBE(file, "CNT3", LEVEL_CHUNK_CNT3_HEADER + envelope_len);
1666     SaveLevel_CNT3(file, level, EL_ENVELOPE);
1667   }
1668
1669   /* check for non-default custom elements (unless using template level) */
1670   if (!level->use_custom_template)
1671   {
1672     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1673     {
1674       int element = EL_CUSTOM_START + i;
1675
1676       if (element_info[element].modified_settings)
1677       {
1678         int num_change_pages = element_info[element].num_change_pages;
1679
1680         putFileChunkBE(file, "CUS4", LEVEL_CHUNK_CUS4_SIZE(num_change_pages));
1681         SaveLevel_CUS4(file, level, element);
1682       }
1683     }
1684   }
1685
1686   fclose(file);
1687
1688   SetFilePermissions(filename, PERMS_PRIVATE);
1689 }
1690
1691 void SaveLevel(int level_nr)
1692 {
1693   char *filename = getLevelFilename(level_nr);
1694
1695   SaveLevelFromFilename(&level, filename);
1696 }
1697
1698 void SaveLevelTemplate()
1699 {
1700   char *filename = getLevelFilename(-1);
1701
1702   SaveLevelFromFilename(&level, filename);
1703 }
1704
1705 void DumpLevel(struct LevelInfo *level)
1706 {
1707   printf_line("-", 79);
1708   printf("Level xxx (file version %08d, game version %08d)\n",
1709          level->file_version, level->game_version);
1710   printf_line("-", 79);
1711
1712   printf("Level Author: '%s'\n", level->author);
1713   printf("Level Title:  '%s'\n", level->name);
1714   printf("\n");
1715   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1716   printf("\n");
1717   printf("Level Time:  %d seconds\n", level->time);
1718   printf("Gems needed: %d\n", level->gems_needed);
1719   printf("\n");
1720   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1721   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
1722   printf("Time for Light:      %d seconds\n", level->time_light);
1723   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
1724   printf("\n");
1725   printf("Amoeba Speed: %d\n", level->amoeba_speed);
1726   printf("\n");
1727   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
1728   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
1729   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1730
1731   printf_line("-", 79);
1732 }
1733
1734
1735 /* ========================================================================= */
1736 /* tape file functions                                                       */
1737 /* ========================================================================= */
1738
1739 static void setTapeInfoToDefaults()
1740 {
1741   int i;
1742
1743   /* always start with reliable default values (empty tape) */
1744   TapeErase();
1745
1746   /* default values (also for pre-1.2 tapes) with only the first player */
1747   tape.player_participates[0] = TRUE;
1748   for(i=1; i<MAX_PLAYERS; i++)
1749     tape.player_participates[i] = FALSE;
1750
1751   /* at least one (default: the first) player participates in every tape */
1752   tape.num_participating_players = 1;
1753
1754   tape.level_nr = level_nr;
1755   tape.counter = 0;
1756   tape.changed = FALSE;
1757
1758   tape.recording = FALSE;
1759   tape.playing = FALSE;
1760   tape.pausing = FALSE;
1761 }
1762
1763 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1764 {
1765   tape->file_version = getFileVersion(file);
1766   tape->game_version = getFileVersion(file);
1767
1768   return chunk_size;
1769 }
1770
1771 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1772 {
1773   int i;
1774
1775   tape->random_seed = getFile32BitBE(file);
1776   tape->date        = getFile32BitBE(file);
1777   tape->length      = getFile32BitBE(file);
1778
1779   /* read header fields that are new since version 1.2 */
1780   if (tape->file_version >= FILE_VERSION_1_2)
1781   {
1782     byte store_participating_players = getFile8Bit(file);
1783     int engine_version;
1784
1785     /* since version 1.2, tapes store which players participate in the tape */
1786     tape->num_participating_players = 0;
1787     for(i=0; i<MAX_PLAYERS; i++)
1788     {
1789       tape->player_participates[i] = FALSE;
1790
1791       if (store_participating_players & (1 << i))
1792       {
1793         tape->player_participates[i] = TRUE;
1794         tape->num_participating_players++;
1795       }
1796     }
1797
1798     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1799
1800     engine_version = getFileVersion(file);
1801     if (engine_version > 0)
1802       tape->engine_version = engine_version;
1803     else
1804       tape->engine_version = tape->game_version;
1805   }
1806
1807   return chunk_size;
1808 }
1809
1810 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1811 {
1812   int level_identifier_size;
1813   int i;
1814
1815   level_identifier_size = getFile16BitBE(file);
1816
1817   tape->level_identifier =
1818     checked_realloc(tape->level_identifier, level_identifier_size);
1819
1820   for(i=0; i < level_identifier_size; i++)
1821     tape->level_identifier[i] = getFile8Bit(file);
1822
1823   tape->level_nr = getFile16BitBE(file);
1824
1825   chunk_size = 2 + level_identifier_size + 2;
1826
1827   return chunk_size;
1828 }
1829
1830 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1831 {
1832   int i, j;
1833   int chunk_size_expected =
1834     (tape->num_participating_players + 1) * tape->length;
1835
1836   if (chunk_size_expected != chunk_size)
1837   {
1838     ReadUnusedBytesFromFile(file, chunk_size);
1839     return chunk_size_expected;
1840   }
1841
1842   for(i=0; i<tape->length; i++)
1843   {
1844     if (i >= MAX_TAPELEN)
1845       break;
1846
1847     for(j=0; j<MAX_PLAYERS; j++)
1848     {
1849       tape->pos[i].action[j] = MV_NO_MOVING;
1850
1851       if (tape->player_participates[j])
1852         tape->pos[i].action[j] = getFile8Bit(file);
1853     }
1854
1855     tape->pos[i].delay = getFile8Bit(file);
1856
1857     if (tape->file_version == FILE_VERSION_1_0)
1858     {
1859       /* eliminate possible diagonal moves in old tapes */
1860       /* this is only for backward compatibility */
1861
1862       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1863       byte action = tape->pos[i].action[0];
1864       int k, num_moves = 0;
1865
1866       for (k=0; k<4; k++)
1867       {
1868         if (action & joy_dir[k])
1869         {
1870           tape->pos[i + num_moves].action[0] = joy_dir[k];
1871           if (num_moves > 0)
1872             tape->pos[i + num_moves].delay = 0;
1873           num_moves++;
1874         }
1875       }
1876
1877       if (num_moves > 1)
1878       {
1879         num_moves--;
1880         i += num_moves;
1881         tape->length += num_moves;
1882       }
1883     }
1884     else if (tape->file_version < FILE_VERSION_2_0)
1885     {
1886       /* convert pre-2.0 tapes to new tape format */
1887
1888       if (tape->pos[i].delay > 1)
1889       {
1890         /* action part */
1891         tape->pos[i + 1] = tape->pos[i];
1892         tape->pos[i + 1].delay = 1;
1893
1894         /* delay part */
1895         for(j=0; j<MAX_PLAYERS; j++)
1896           tape->pos[i].action[j] = MV_NO_MOVING;
1897         tape->pos[i].delay--;
1898
1899         i++;
1900         tape->length++;
1901       }
1902     }
1903
1904     if (feof(file))
1905       break;
1906   }
1907
1908   if (i != tape->length)
1909     chunk_size = (tape->num_participating_players + 1) * i;
1910
1911   return chunk_size;
1912 }
1913
1914 void LoadTapeFromFilename(char *filename)
1915 {
1916   char cookie[MAX_LINE_LEN];
1917   char chunk_name[CHUNK_ID_LEN + 1];
1918   FILE *file;
1919   int chunk_size;
1920
1921   /* always start with reliable default values */
1922   setTapeInfoToDefaults();
1923
1924   if (!(file = fopen(filename, MODE_READ)))
1925     return;
1926
1927   getFileChunkBE(file, chunk_name, NULL);
1928   if (strcmp(chunk_name, "RND1") == 0)
1929   {
1930     getFile32BitBE(file);               /* not used */
1931
1932     getFileChunkBE(file, chunk_name, NULL);
1933     if (strcmp(chunk_name, "TAPE") != 0)
1934     {
1935       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1936       fclose(file);
1937       return;
1938     }
1939   }
1940   else  /* check for pre-2.0 file format with cookie string */
1941   {
1942     strcpy(cookie, chunk_name);
1943     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1944     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1945       cookie[strlen(cookie) - 1] = '\0';
1946
1947     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1948     {
1949       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1950       fclose(file);
1951       return;
1952     }
1953
1954     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1955     {
1956       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1957       fclose(file);
1958       return;
1959     }
1960
1961     /* pre-2.0 tape files have no game version, so use file version here */
1962     tape.game_version = tape.file_version;
1963   }
1964
1965   if (tape.file_version < FILE_VERSION_1_2)
1966   {
1967     /* tape files from versions before 1.2.0 without chunk structure */
1968     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1969     LoadTape_BODY(file, 2 * tape.length,  &tape);
1970   }
1971   else
1972   {
1973     static struct
1974     {
1975       char *name;
1976       int size;
1977       int (*loader)(FILE *, int, struct TapeInfo *);
1978     }
1979     chunk_info[] =
1980     {
1981       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1982       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1983       { "INFO", -1,                     LoadTape_INFO },
1984       { "BODY", -1,                     LoadTape_BODY },
1985       {  NULL,  0,                      NULL }
1986     };
1987
1988     while (getFileChunkBE(file, chunk_name, &chunk_size))
1989     {
1990       int i = 0;
1991
1992       while (chunk_info[i].name != NULL &&
1993              strcmp(chunk_name, chunk_info[i].name) != 0)
1994         i++;
1995
1996       if (chunk_info[i].name == NULL)
1997       {
1998         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1999               chunk_name, filename);
2000         ReadUnusedBytesFromFile(file, chunk_size);
2001       }
2002       else if (chunk_info[i].size != -1 &&
2003                chunk_info[i].size != chunk_size)
2004       {
2005         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2006               chunk_size, chunk_name, filename);
2007         ReadUnusedBytesFromFile(file, chunk_size);
2008       }
2009       else
2010       {
2011         /* call function to load this tape chunk */
2012         int chunk_size_expected =
2013           (chunk_info[i].loader)(file, chunk_size, &tape);
2014
2015         /* the size of some chunks cannot be checked before reading other
2016            chunks first (like "HEAD" and "BODY") that contain some header
2017            information, so check them here */
2018         if (chunk_size_expected != chunk_size)
2019         {
2020           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
2021                 chunk_size, chunk_name, filename);
2022         }
2023       }
2024     }
2025   }
2026
2027   fclose(file);
2028
2029   tape.length_seconds = GetTapeLength();
2030
2031 #if 0
2032   printf("tape game version: %d\n", tape.game_version);
2033   printf("tape engine version: %d\n", tape.engine_version);
2034 #endif
2035 }
2036
2037 void LoadTape(int level_nr)
2038 {
2039   char *filename = getTapeFilename(level_nr);
2040
2041   LoadTapeFromFilename(filename);
2042 }
2043
2044 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
2045 {
2046   putFileVersion(file, tape->file_version);
2047   putFileVersion(file, tape->game_version);
2048 }
2049
2050 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
2051 {
2052   int i;
2053   byte store_participating_players = 0;
2054
2055   /* set bits for participating players for compact storage */
2056   for(i=0; i<MAX_PLAYERS; i++)
2057     if (tape->player_participates[i])
2058       store_participating_players |= (1 << i);
2059
2060   putFile32BitBE(file, tape->random_seed);
2061   putFile32BitBE(file, tape->date);
2062   putFile32BitBE(file, tape->length);
2063
2064   putFile8Bit(file, store_participating_players);
2065
2066   /* unused bytes not at the end here for 4-byte alignment of engine_version */
2067   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
2068
2069   putFileVersion(file, tape->engine_version);
2070 }
2071
2072 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
2073 {
2074   int level_identifier_size = strlen(tape->level_identifier) + 1;
2075   int i;
2076
2077   putFile16BitBE(file, level_identifier_size);
2078
2079   for(i=0; i < level_identifier_size; i++)
2080     putFile8Bit(file, tape->level_identifier[i]);
2081
2082   putFile16BitBE(file, tape->level_nr);
2083 }
2084
2085 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
2086 {
2087   int i, j;
2088
2089   for(i=0; i<tape->length; i++)
2090   {
2091     for(j=0; j<MAX_PLAYERS; j++)
2092       if (tape->player_participates[j])
2093         putFile8Bit(file, tape->pos[i].action[j]);
2094
2095     putFile8Bit(file, tape->pos[i].delay);
2096   }
2097 }
2098
2099 void SaveTape(int level_nr)
2100 {
2101   char *filename = getTapeFilename(level_nr);
2102   FILE *file;
2103   boolean new_tape = TRUE;
2104   int num_participating_players = 0;
2105   int info_chunk_size;
2106   int body_chunk_size;
2107   int i;
2108
2109   InitTapeDirectory(leveldir_current->filename);
2110
2111   /* if a tape still exists, ask to overwrite it */
2112   if (access(filename, F_OK) == 0)
2113   {
2114     new_tape = FALSE;
2115     if (!Request("Replace old tape ?", REQ_ASK))
2116       return;
2117   }
2118
2119   if (!(file = fopen(filename, MODE_WRITE)))
2120   {
2121     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
2122     return;
2123   }
2124
2125   tape.file_version = FILE_VERSION_ACTUAL;
2126   tape.game_version = GAME_VERSION_ACTUAL;
2127
2128   /* count number of participating players  */
2129   for(i=0; i<MAX_PLAYERS; i++)
2130     if (tape.player_participates[i])
2131       num_participating_players++;
2132
2133   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
2134   body_chunk_size = (num_participating_players + 1) * tape.length;
2135
2136   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
2137   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
2138
2139   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
2140   SaveTape_VERS(file, &tape);
2141
2142   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
2143   SaveTape_HEAD(file, &tape);
2144
2145   putFileChunkBE(file, "INFO", info_chunk_size);
2146   SaveTape_INFO(file, &tape);
2147
2148   putFileChunkBE(file, "BODY", body_chunk_size);
2149   SaveTape_BODY(file, &tape);
2150
2151   fclose(file);
2152
2153   SetFilePermissions(filename, PERMS_PRIVATE);
2154
2155   tape.changed = FALSE;
2156
2157   if (new_tape)
2158     Request("tape saved !", REQ_CONFIRM);
2159 }
2160
2161 void DumpTape(struct TapeInfo *tape)
2162 {
2163   int i, j;
2164
2165   if (TAPE_IS_EMPTY(*tape))
2166   {
2167     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
2168     return;
2169   }
2170
2171   printf_line("-", 79);
2172   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
2173          tape->level_nr, tape->file_version, tape->game_version);
2174   printf("Level series identifier: '%s'\n", tape->level_identifier);
2175   printf_line("-", 79);
2176
2177   for(i=0; i<tape->length; i++)
2178   {
2179     if (i >= MAX_TAPELEN)
2180       break;
2181
2182     printf("%03d: ", i);
2183
2184     for(j=0; j<MAX_PLAYERS; j++)
2185     {
2186       if (tape->player_participates[j])
2187       {
2188         int action = tape->pos[i].action[j];
2189
2190         printf("%d:%02x ", j, action);
2191         printf("[%c%c%c%c|%c%c] - ",
2192                (action & JOY_LEFT ? '<' : ' '),
2193                (action & JOY_RIGHT ? '>' : ' '),
2194                (action & JOY_UP ? '^' : ' '),
2195                (action & JOY_DOWN ? 'v' : ' '),
2196                (action & JOY_BUTTON_1 ? '1' : ' '),
2197                (action & JOY_BUTTON_2 ? '2' : ' '));
2198       }
2199     }
2200
2201     printf("(%03d)\n", tape->pos[i].delay);
2202   }
2203
2204   printf_line("-", 79);
2205 }
2206
2207
2208 /* ========================================================================= */
2209 /* score file functions                                                      */
2210 /* ========================================================================= */
2211
2212 void LoadScore(int level_nr)
2213 {
2214   int i;
2215   char *filename = getScoreFilename(level_nr);
2216   char cookie[MAX_LINE_LEN];
2217   char line[MAX_LINE_LEN];
2218   char *line_ptr;
2219   FILE *file;
2220
2221   /* always start with reliable default values */
2222   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2223   {
2224     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
2225     highscore[i].Score = 0;
2226   }
2227
2228   if (!(file = fopen(filename, MODE_READ)))
2229     return;
2230
2231   /* check file identifier */
2232   fgets(cookie, MAX_LINE_LEN, file);
2233   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
2234     cookie[strlen(cookie) - 1] = '\0';
2235
2236   if (!checkCookieString(cookie, SCORE_COOKIE))
2237   {
2238     Error(ERR_WARN, "unknown format of score file '%s'", filename);
2239     fclose(file);
2240     return;
2241   }
2242
2243   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2244   {
2245     fscanf(file, "%d", &highscore[i].Score);
2246     fgets(line, MAX_LINE_LEN, file);
2247
2248     if (line[strlen(line) - 1] == '\n')
2249       line[strlen(line) - 1] = '\0';
2250
2251     for (line_ptr = line; *line_ptr; line_ptr++)
2252     {
2253       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
2254       {
2255         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
2256         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
2257         break;
2258       }
2259     }
2260   }
2261
2262   fclose(file);
2263 }
2264
2265 void SaveScore(int level_nr)
2266 {
2267   int i;
2268   char *filename = getScoreFilename(level_nr);
2269   FILE *file;
2270
2271   InitScoreDirectory(leveldir_current->filename);
2272
2273   if (!(file = fopen(filename, MODE_WRITE)))
2274   {
2275     Error(ERR_WARN, "cannot save score for level %d", level_nr);
2276     return;
2277   }
2278
2279   fprintf(file, "%s\n\n", SCORE_COOKIE);
2280
2281   for(i=0; i<MAX_SCORE_ENTRIES; i++)
2282     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
2283
2284   fclose(file);
2285
2286   SetFilePermissions(filename, PERMS_PUBLIC);
2287 }
2288
2289
2290 /* ========================================================================= */
2291 /* setup file functions                                                      */
2292 /* ========================================================================= */
2293
2294 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
2295
2296 /* global setup */
2297 #define SETUP_TOKEN_PLAYER_NAME                 0
2298 #define SETUP_TOKEN_SOUND                       1
2299 #define SETUP_TOKEN_SOUND_LOOPS                 2
2300 #define SETUP_TOKEN_SOUND_MUSIC                 3
2301 #define SETUP_TOKEN_SOUND_SIMPLE                4
2302 #define SETUP_TOKEN_TOONS                       5
2303 #define SETUP_TOKEN_SCROLL_DELAY                6
2304 #define SETUP_TOKEN_SOFT_SCROLLING              7
2305 #define SETUP_TOKEN_FADING                      8
2306 #define SETUP_TOKEN_AUTORECORD                  9
2307 #define SETUP_TOKEN_QUICK_DOORS                 10
2308 #define SETUP_TOKEN_TEAM_MODE                   11
2309 #define SETUP_TOKEN_HANDICAP                    12
2310 #define SETUP_TOKEN_TIME_LIMIT                  13
2311 #define SETUP_TOKEN_FULLSCREEN                  14
2312 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
2313 #define SETUP_TOKEN_GRAPHICS_SET                16
2314 #define SETUP_TOKEN_SOUNDS_SET                  17
2315 #define SETUP_TOKEN_MUSIC_SET                   18
2316 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
2317 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
2318 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
2319
2320 #define NUM_GLOBAL_SETUP_TOKENS                 22
2321
2322 /* editor setup */
2323 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
2324 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
2325 #define SETUP_TOKEN_EDITOR_EL_MORE              2
2326 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
2327 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
2328 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
2329 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
2330 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
2331 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
2332 #define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE       9
2333 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         10
2334
2335 #define NUM_EDITOR_SETUP_TOKENS                 11
2336
2337 /* shortcut setup */
2338 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
2339 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
2340 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
2341
2342 #define NUM_SHORTCUT_SETUP_TOKENS               3
2343
2344 /* player setup */
2345 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
2346 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
2347 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
2348 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
2349 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
2350 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
2351 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
2352 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
2353 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
2354 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
2355 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
2356 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
2357 #define SETUP_TOKEN_PLAYER_KEY_UP               12
2358 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
2359 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
2360 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
2361
2362 #define NUM_PLAYER_SETUP_TOKENS                 16
2363
2364 /* system setup */
2365 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
2366 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
2367
2368 #define NUM_SYSTEM_SETUP_TOKENS                 2
2369
2370 /* options setup */
2371 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
2372
2373 #define NUM_OPTIONS_SETUP_TOKENS                1
2374
2375
2376 static struct SetupInfo si;
2377 static struct SetupEditorInfo sei;
2378 static struct SetupShortcutInfo ssi;
2379 static struct SetupInputInfo sii;
2380 static struct SetupSystemInfo syi;
2381 static struct OptionInfo soi;
2382
2383 static struct TokenInfo global_setup_tokens[] =
2384 {
2385   { TYPE_STRING, &si.player_name,       "player_name"                   },
2386   { TYPE_SWITCH, &si.sound,             "sound"                         },
2387   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
2388   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
2389   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
2390   { TYPE_SWITCH, &si.toons,             "toons"                         },
2391   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
2392   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
2393   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
2394   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
2395   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
2396   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
2397   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
2398   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
2399   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
2400   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
2401   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
2402   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
2403   { TYPE_STRING, &si.music_set,         "music_set"                     },
2404   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
2405   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
2406   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
2407 };
2408
2409 static struct TokenInfo editor_setup_tokens[] =
2410 {
2411   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
2412   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
2413   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
2414   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
2415   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
2416   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
2417   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
2418   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
2419   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
2420   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
2421   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
2422 };
2423
2424 static struct TokenInfo shortcut_setup_tokens[] =
2425 {
2426   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
2427   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
2428   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
2429 };
2430
2431 static struct TokenInfo player_setup_tokens[] =
2432 {
2433   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
2434   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
2435   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
2436   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
2437   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
2438   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
2439   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
2440   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
2441   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
2442   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
2443   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
2444   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
2445   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
2446   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
2447   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
2448   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
2449 };
2450
2451 static struct TokenInfo system_setup_tokens[] =
2452 {
2453   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
2454   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
2455 };
2456
2457 static struct TokenInfo options_setup_tokens[] =
2458 {
2459   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2460 };
2461
2462 static char *get_corrected_login_name(char *login_name)
2463 {
2464   /* needed because player name must be a fixed length string */
2465   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2466
2467   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2468   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2469
2470   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2471     if (strchr(login_name_new, ' '))
2472       *strchr(login_name_new, ' ') = '\0';
2473
2474   return login_name_new;
2475 }
2476
2477 static void setSetupInfoToDefaults(struct SetupInfo *si)
2478 {
2479   int i;
2480
2481   si->player_name = get_corrected_login_name(getLoginName());
2482
2483   si->sound = TRUE;
2484   si->sound_loops = TRUE;
2485   si->sound_music = TRUE;
2486   si->sound_simple = TRUE;
2487   si->toons = TRUE;
2488   si->double_buffering = TRUE;
2489   si->direct_draw = !si->double_buffering;
2490   si->scroll_delay = TRUE;
2491   si->soft_scrolling = TRUE;
2492   si->fading = FALSE;
2493   si->autorecord = TRUE;
2494   si->quick_doors = FALSE;
2495   si->team_mode = FALSE;
2496   si->handicap = TRUE;
2497   si->time_limit = TRUE;
2498   si->fullscreen = FALSE;
2499   si->ask_on_escape = TRUE;
2500
2501   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2502   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2503   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2504   si->override_level_graphics = FALSE;
2505   si->override_level_sounds = FALSE;
2506   si->override_level_music = FALSE;
2507
2508   si->editor.el_boulderdash = TRUE;
2509   si->editor.el_emerald_mine = TRUE;
2510   si->editor.el_more = TRUE;
2511   si->editor.el_sokoban = TRUE;
2512   si->editor.el_supaplex = TRUE;
2513   si->editor.el_diamond_caves = TRUE;
2514   si->editor.el_dx_boulderdash = TRUE;
2515   si->editor.el_chars = TRUE;
2516   si->editor.el_custom = TRUE;
2517   si->editor.el_custom_more = FALSE;
2518
2519   si->editor.el_headlines = TRUE;
2520
2521   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2522   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2523   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2524
2525   for (i=0; i<MAX_PLAYERS; i++)
2526   {
2527     si->input[i].use_joystick = FALSE;
2528     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2529     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2530     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2531     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2532     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2533     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2534     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2535     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2536     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2537     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2538     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2539     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2540     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2541     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2542     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2543   }
2544
2545   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2546   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2547
2548   si->options.verbose = FALSE;
2549 }
2550
2551 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2552 {
2553   int i, pnr;
2554
2555   if (!setup_file_hash)
2556     return;
2557
2558   /* global setup */
2559   si = setup;
2560   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2561     setSetupInfo(global_setup_tokens, i,
2562                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2563   setup = si;
2564
2565   /* editor setup */
2566   sei = setup.editor;
2567   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2568     setSetupInfo(editor_setup_tokens, i,
2569                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2570   setup.editor = sei;
2571
2572   /* shortcut setup */
2573   ssi = setup.shortcut;
2574   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2575     setSetupInfo(shortcut_setup_tokens, i,
2576                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2577   setup.shortcut = ssi;
2578
2579   /* player setup */
2580   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2581   {
2582     char prefix[30];
2583
2584     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2585
2586     sii = setup.input[pnr];
2587     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2588     {
2589       char full_token[100];
2590
2591       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2592       setSetupInfo(player_setup_tokens, i,
2593                    getHashEntry(setup_file_hash, full_token));
2594     }
2595     setup.input[pnr] = sii;
2596   }
2597
2598   /* system setup */
2599   syi = setup.system;
2600   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2601     setSetupInfo(system_setup_tokens, i,
2602                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2603   setup.system = syi;
2604
2605   /* options setup */
2606   soi = setup.options;
2607   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2608     setSetupInfo(options_setup_tokens, i,
2609                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2610   setup.options = soi;
2611 }
2612
2613 void LoadSetup()
2614 {
2615   char *filename = getSetupFilename();
2616   SetupFileHash *setup_file_hash = NULL;
2617
2618   /* always start with reliable default values */
2619   setSetupInfoToDefaults(&setup);
2620
2621   setup_file_hash = loadSetupFileHash(filename);
2622
2623   if (setup_file_hash)
2624   {
2625     char *player_name_new;
2626
2627     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2628     decodeSetupFileHash(setup_file_hash);
2629
2630     setup.direct_draw = !setup.double_buffering;
2631
2632     freeSetupFileHash(setup_file_hash);
2633
2634     /* needed to work around problems with fixed length strings */
2635     player_name_new = get_corrected_login_name(setup.player_name);
2636     free(setup.player_name);
2637     setup.player_name = player_name_new;
2638   }
2639   else
2640     Error(ERR_WARN, "using default setup values");
2641 }
2642
2643 void SaveSetup()
2644 {
2645   char *filename = getSetupFilename();
2646   FILE *file;
2647   int i, pnr;
2648
2649   InitUserDataDirectory();
2650
2651   if (!(file = fopen(filename, MODE_WRITE)))
2652   {
2653     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2654     return;
2655   }
2656
2657   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2658                                                getCookie("SETUP")));
2659   fprintf(file, "\n");
2660
2661   /* global setup */
2662   si = setup;
2663   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2664   {
2665     /* just to make things nicer :) */
2666     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2667         i == SETUP_TOKEN_GRAPHICS_SET)
2668       fprintf(file, "\n");
2669
2670     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2671   }
2672
2673   /* editor setup */
2674   sei = setup.editor;
2675   fprintf(file, "\n");
2676   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2677     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2678
2679   /* shortcut setup */
2680   ssi = setup.shortcut;
2681   fprintf(file, "\n");
2682   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2683     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2684
2685   /* player setup */
2686   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2687   {
2688     char prefix[30];
2689
2690     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2691     fprintf(file, "\n");
2692
2693     sii = setup.input[pnr];
2694     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2695       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2696   }
2697
2698   /* system setup */
2699   syi = setup.system;
2700   fprintf(file, "\n");
2701   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2702     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2703
2704   /* options setup */
2705   soi = setup.options;
2706   fprintf(file, "\n");
2707   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2708     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2709
2710   fclose(file);
2711
2712   SetFilePermissions(filename, PERMS_PRIVATE);
2713 }
2714
2715 void LoadCustomElementDescriptions()
2716 {
2717   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2718   SetupFileHash *setup_file_hash;
2719   int i;
2720
2721   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2722   {
2723     if (element_info[i].custom_description != NULL)
2724     {
2725       free(element_info[i].custom_description);
2726       element_info[i].custom_description = NULL;
2727     }
2728   }
2729
2730   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2731     return;
2732
2733   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2734   {
2735     char *token = getStringCat2(element_info[i].token_name, ".name");
2736     char *value = getHashEntry(setup_file_hash, token);
2737
2738     if (value != NULL)
2739       element_info[i].custom_description = getStringCopy(value);
2740
2741     free(token);
2742   }
2743
2744   freeSetupFileHash(setup_file_hash);
2745 }
2746
2747 void LoadSpecialMenuDesignSettings()
2748 {
2749   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2750   SetupFileHash *setup_file_hash;
2751   int i, j;
2752
2753   /* always start with reliable default values from default config */
2754   for (i=0; image_config_vars[i].token != NULL; i++)
2755     for (j=0; image_config[j].token != NULL; j++)
2756       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2757         *image_config_vars[i].value =
2758           get_integer_from_string(image_config[j].value);
2759
2760   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2761     return;
2762
2763   /* special case: initialize with default values that may be overwritten */
2764   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2765   {
2766     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2767     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2768     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2769
2770     if (value_x != NULL)
2771       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2772     if (value_y != NULL)
2773       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2774     if (list_size != NULL)
2775       menu.list_size[i] = get_integer_from_string(list_size);
2776   }
2777
2778   /* read (and overwrite with) values that may be specified in config file */
2779   for (i=0; image_config_vars[i].token != NULL; i++)
2780   {
2781     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2782
2783     if (value != NULL)
2784       *image_config_vars[i].value = get_integer_from_string(value);
2785   }
2786
2787   freeSetupFileHash(setup_file_hash);
2788 }