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