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