36380f92eef55a65368f4c6447240adb6680d06d
[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 "tools.h"
21 #include "tape.h"
22
23
24 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
25 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
26 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
27 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
29 #define LEVEL_HEADER_UNUSED     14      /* unused level header bytes  */
30 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
32 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
33 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
34
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39
40
41 /* ========================================================================= */
42 /* level file functions                                                      */
43 /* ========================================================================= */
44
45 static void setLevelInfoToDefaults()
46 {
47   int i, x, y;
48
49   level.file_version = FILE_VERSION_ACTUAL;
50   level.game_version = GAME_VERSION_ACTUAL;
51
52   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
53   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
54   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
55
56   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
58
59   for(x=0; x<MAX_LEV_FIELDX; x++)
60     for(y=0; y<MAX_LEV_FIELDY; y++)
61       Feld[x][y] = Ur[x][y] = EL_SAND;
62
63   level.time = 100;
64   level.gems_needed = 0;
65   level.amoeba_speed = 10;
66   level.time_magic_wall = 10;
67   level.time_wheel = 10;
68   level.time_light = 10;
69   level.time_timegate = 10;
70   level.amoeba_content = EL_DIAMOND;
71   level.double_speed = FALSE;
72   level.gravity = FALSE;
73   level.em_slippery_gems = FALSE;
74
75   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
76     level.name[i] = '\0';
77   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78     level.author[i] = '\0';
79
80   strcpy(level.name, NAMELESS_LEVEL_NAME);
81   strcpy(level.author, ANONYMOUS_NAME);
82
83   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
84     level.score[i] = 10;
85
86   level.num_yam_contents = STD_ELEMENT_CONTENTS;
87   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
88     for(x=0; x<3; x++)
89       for(y=0; y<3; y++)
90         level.yam_content[i][x][y] =
91           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
92
93   Feld[0][0] = Ur[0][0] = EL_PLAYER_1;
94   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
96
97   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
98   {
99     level.custom_element_successor[i] = EL_EMPTY_SPACE;
100     Properties[EL_CUSTOM_START + i][EP_BITFIELD_BASE] = EP_BITMASK_DEFAULT;
101   }
102
103   BorderElement = EL_STEELWALL;
104
105   level.no_level_file = FALSE;
106
107   if (leveldir_current == NULL)         /* only when dumping level */
108     return;
109
110   /* try to determine better author name than 'anonymous' */
111   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
112   {
113     strncpy(level.author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
114     level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
115   }
116   else
117   {
118     switch (LEVELCLASS(leveldir_current))
119     {
120       case LEVELCLASS_TUTORIAL:
121         strcpy(level.author, PROGRAM_AUTHOR_STRING);
122         break;
123
124       case LEVELCLASS_CONTRIBUTION:
125         strncpy(level.author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
126         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
127         break;
128
129       case LEVELCLASS_USER:
130         strncpy(level.author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
131         level.author[MAX_LEVEL_AUTHOR_LEN] = '\0';
132         break;
133
134       default:
135         /* keep default value */
136         break;
137     }
138   }
139 }
140
141 static int checkLevelElement(int element)
142 {
143   if (element >= NUM_FILE_ELEMENTS)
144   {
145     Error(ERR_WARN, "invalid level element %d", element);
146     element = EL_CHAR_QUESTION;
147   }
148   else if (element == EL_PLAYER_OBSOLETE)
149     element = EL_PLAYER_1;
150   else if (element == EL_KEY_OBSOLETE)
151     element = EL_KEY_1;
152
153   return element;
154 }
155
156 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
157 {
158   level->file_version = getFileVersion(file);
159   level->game_version = getFileVersion(file);
160
161   return chunk_size;
162 }
163
164 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
165 {
166   int i, x, y;
167
168   lev_fieldx = level->fieldx = fgetc(file);
169   lev_fieldy = level->fieldy = fgetc(file);
170
171   level->time           = getFile16BitBE(file);
172   level->gems_needed    = getFile16BitBE(file);
173
174   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
175     level->name[i] = fgetc(file);
176   level->name[MAX_LEVEL_NAME_LEN] = 0;
177
178   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
179     level->score[i] = fgetc(file);
180
181   level->num_yam_contents = STD_ELEMENT_CONTENTS;
182   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
183     for(y=0; y<3; y++)
184       for(x=0; x<3; x++)
185         level->yam_content[i][x][y] = checkLevelElement(fgetc(file));
186
187   level->amoeba_speed           = fgetc(file);
188   level->time_magic_wall        = fgetc(file);
189   level->time_wheel             = fgetc(file);
190   level->amoeba_content         = checkLevelElement(fgetc(file));
191   level->double_speed           = (fgetc(file) == 1 ? TRUE : FALSE);
192   level->gravity                = (fgetc(file) == 1 ? TRUE : FALSE);
193   level->encoding_16bit_field   = (fgetc(file) == 1 ? TRUE : FALSE);
194   level->em_slippery_gems       = (fgetc(file) == 1 ? TRUE : FALSE);
195
196   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
197
198   return chunk_size;
199 }
200
201 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
202 {
203   int i;
204
205   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
206     level->author[i] = fgetc(file);
207   level->author[MAX_LEVEL_NAME_LEN] = 0;
208
209   return chunk_size;
210 }
211
212 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
213 {
214   int x, y;
215   int chunk_size_expected = level->fieldx * level->fieldy;
216
217   /* Note: "chunk_size" was wrong before version 2.0 when elements are
218      stored with 16-bit encoding (and should be twice as big then).
219      Even worse, playfield data was stored 16-bit when only yamyam content
220      contained 16-bit elements and vice versa. */
221
222   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
223     chunk_size_expected *= 2;
224
225   if (chunk_size_expected != chunk_size)
226   {
227     ReadUnusedBytesFromFile(file, chunk_size);
228     return chunk_size_expected;
229   }
230
231   for(y=0; y<level->fieldy; y++)
232     for(x=0; x<level->fieldx; x++)
233       Feld[x][y] = Ur[x][y] =
234         checkLevelElement(level->encoding_16bit_field ?
235                           getFile16BitBE(file) : fgetc(file));
236   return chunk_size;
237 }
238
239 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
240 {
241   int i, x, y;
242   int header_size = 4;
243   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
244   int chunk_size_expected = header_size + content_size;
245
246   /* Note: "chunk_size" was wrong before version 2.0 when elements are
247      stored with 16-bit encoding (and should be twice as big then).
248      Even worse, playfield data was stored 16-bit when only yamyam content
249      contained 16-bit elements and vice versa. */
250
251   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
252     chunk_size_expected += content_size;
253
254   if (chunk_size_expected != chunk_size)
255   {
256     ReadUnusedBytesFromFile(file, chunk_size);
257     return chunk_size_expected;
258   }
259
260   fgetc(file);
261   level->num_yam_contents = fgetc(file);
262   fgetc(file);
263   fgetc(file);
264
265   /* correct invalid number of content fields -- should never happen */
266   if (level->num_yam_contents < 1 ||
267       level->num_yam_contents > MAX_ELEMENT_CONTENTS)
268     level->num_yam_contents = STD_ELEMENT_CONTENTS;
269
270   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
271     for(y=0; y<3; y++)
272       for(x=0; x<3; x++)
273         level->yam_content[i][x][y] =
274           checkLevelElement(level->encoding_16bit_field ?
275                             getFile16BitBE(file) : fgetc(file));
276   return chunk_size;
277 }
278
279 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
280 {
281   int i, x, y;
282   int element;
283   int num_contents, content_xsize, content_ysize;
284   int content_array[MAX_ELEMENT_CONTENTS][3][3];
285
286   element = checkLevelElement(getFile16BitBE(file));
287   num_contents = fgetc(file);
288   content_xsize = fgetc(file);
289   content_ysize = fgetc(file);
290   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
291
292   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
293     for(y=0; y<3; y++)
294       for(x=0; x<3; x++)
295         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
296
297   /* correct invalid number of content fields -- should never happen */
298   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
299     num_contents = STD_ELEMENT_CONTENTS;
300
301   if (element == EL_YAMYAM)
302   {
303     level->num_yam_contents = num_contents;
304
305     for(i=0; i<num_contents; i++)
306       for(y=0; y<3; y++)
307         for(x=0; x<3; x++)
308           level->yam_content[i][x][y] = content_array[i][x][y];
309   }
310   else if (element == EL_BD_AMOEBA)
311   {
312     level->amoeba_content = content_array[0][0][0];
313   }
314   else
315   {
316     Error(ERR_WARN, "cannot load content for element '%d'", element);
317   }
318
319   return chunk_size;
320 }
321
322 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
323 {
324   int num_changed_custom_elements = getFile16BitBE(file);
325   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
326   int i;
327
328   if (chunk_size_expected != chunk_size)
329   {
330     ReadUnusedBytesFromFile(file, chunk_size - 2);
331     return chunk_size_expected;
332   }
333
334   for (i=0; i < num_changed_custom_elements; i++)
335   {
336     int element = getFile16BitBE(file);
337     int properties = getFile32BitBE(file);
338
339     if (IS_CUSTOM_ELEMENT(element))
340       Properties[element][EP_BITFIELD_BASE] = properties;
341     else
342       Error(ERR_WARN, "invalid custom element number %d", element);
343   }
344
345   return chunk_size;
346 }
347
348 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
349 {
350   int num_changed_custom_elements = getFile16BitBE(file);
351   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
352   int i;
353
354   if (chunk_size_expected != chunk_size)
355   {
356     ReadUnusedBytesFromFile(file, chunk_size - 2);
357     return chunk_size_expected;
358   }
359
360   for (i=0; i < num_changed_custom_elements; i++)
361   {
362     int element = getFile16BitBE(file);
363     int custom_element_successor = getFile16BitBE(file);
364     int i = element - EL_CUSTOM_START;
365
366     if (IS_CUSTOM_ELEMENT(element))
367       level->custom_element_successor[i] = custom_element_successor;
368     else
369       Error(ERR_WARN, "invalid custom element number %d", element);
370   }
371
372   return chunk_size;
373 }
374
375 void LoadLevelFromFilename(char *filename)
376 {
377   char cookie[MAX_LINE_LEN];
378   char chunk_name[CHUNK_ID_LEN + 1];
379   int chunk_size;
380   FILE *file;
381
382   /* always start with reliable default values */
383   setLevelInfoToDefaults();
384
385   if (!(file = fopen(filename, MODE_READ)))
386   {
387     level.no_level_file = TRUE;
388
389     Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
390     return;
391   }
392
393   getFileChunkBE(file, chunk_name, NULL);
394   if (strcmp(chunk_name, "RND1") == 0)
395   {
396     getFile32BitBE(file);               /* not used */
397
398     getFileChunkBE(file, chunk_name, NULL);
399     if (strcmp(chunk_name, "CAVE") != 0)
400     {
401       Error(ERR_WARN, "unknown format of level file '%s'", filename);
402       fclose(file);
403       return;
404     }
405   }
406   else  /* check for pre-2.0 file format with cookie string */
407   {
408     strcpy(cookie, chunk_name);
409     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
410     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
411       cookie[strlen(cookie) - 1] = '\0';
412
413     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
414     {
415       Error(ERR_WARN, "unknown format of level file '%s'", filename);
416       fclose(file);
417       return;
418     }
419
420     if ((level.file_version = getFileVersionFromCookieString(cookie)) == -1)
421     {
422       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
423       fclose(file);
424       return;
425     }
426
427     /* pre-2.0 level files have no game version, so use file version here */
428     level.game_version = level.file_version;
429   }
430
431   if (level.file_version < FILE_VERSION_1_2)
432   {
433     /* level files from versions before 1.2.0 without chunk structure */
434     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,           &level);
435     LoadLevel_BODY(file, level.fieldx * level.fieldy, &level);
436   }
437   else
438   {
439     static struct
440     {
441       char *name;
442       int size;
443       int (*loader)(FILE *, int, struct LevelInfo *);
444     }
445     chunk_info[] =
446     {
447       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
448       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
449       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
450       { "BODY", -1,                     LoadLevel_BODY },
451       { "CONT", -1,                     LoadLevel_CONT },
452       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
453       { "CUS1", -1,                     LoadLevel_CUS1 },
454       { "CUS2", -1,                     LoadLevel_CUS2 },
455       {  NULL,  0,                      NULL }
456     };
457
458     while (getFileChunkBE(file, chunk_name, &chunk_size))
459     {
460       int i = 0;
461
462       while (chunk_info[i].name != NULL &&
463              strcmp(chunk_name, chunk_info[i].name) != 0)
464         i++;
465
466       if (chunk_info[i].name == NULL)
467       {
468         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
469               chunk_name, filename);
470         ReadUnusedBytesFromFile(file, chunk_size);
471       }
472       else if (chunk_info[i].size != -1 &&
473                chunk_info[i].size != chunk_size)
474       {
475         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
476               chunk_size, chunk_name, filename);
477         ReadUnusedBytesFromFile(file, chunk_size);
478       }
479       else
480       {
481         /* call function to load this level chunk */
482         int chunk_size_expected =
483           (chunk_info[i].loader)(file, chunk_size, &level);
484
485         /* the size of some chunks cannot be checked before reading other
486            chunks first (like "HEAD" and "BODY") that contain some header
487            information, so check them here */
488         if (chunk_size_expected != chunk_size)
489         {
490           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
491                 chunk_size, chunk_name, filename);
492         }
493       }
494     }
495   }
496
497   fclose(file);
498
499   if (leveldir_current == NULL)         /* only when dumping level */
500     return;
501
502   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
503       IS_LEVELCLASS_USER(leveldir_current))
504   {
505     /* For user contributed and private levels, use the version of
506        the game engine the levels were created for.
507        Since 2.0.1, the game engine version is now directly stored
508        in the level file (chunk "VERS"), so there is no need anymore
509        to set the game version from the file version (except for old,
510        pre-2.0 levels, where the game version is still taken from the
511        file format version used to store the level -- see above). */
512
513     /* do some special adjustments to support older level versions */
514     if (level.file_version == FILE_VERSION_1_0)
515     {
516       Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
517       Error(ERR_WARN, "using high speed movement for player");
518
519       /* player was faster than monsters in (pre-)1.0 levels */
520       level.double_speed = TRUE;
521     }
522
523     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
524     if (level.game_version == VERSION_IDENT(2,0,1))
525       level.em_slippery_gems = TRUE;
526   }
527   else
528   {
529     /* Always use the latest version of the game engine for all but
530        user contributed and private levels; this allows for actual
531        corrections in the game engine to take effect for existing,
532        converted levels (from "classic" or other existing games) to
533        make the game emulation more accurate, while (hopefully) not
534        breaking existing levels created from other players. */
535
536     level.game_version = GAME_VERSION_ACTUAL;
537
538     /* Set special EM style gems behaviour: EM style gems slip down from
539        normal, steel and growing wall. As this is a more fundamental change,
540        it seems better to set the default behaviour to "off" (as it is more
541        natural) and make it configurable in the level editor (as a property
542        of gem style elements). Already existing converted levels (neither
543        private nor contributed levels) are changed to the new behaviour. */
544
545     if (level.file_version < FILE_VERSION_2_0)
546       level.em_slippery_gems = TRUE;
547   }
548
549   /* map some elements which have changed in newer versions */
550   if (level.game_version <= VERSION_IDENT(2,2,0))
551   {
552     int x, y;
553
554     /* map game font elements */
555     for(y=0; y<level.fieldy; y++)
556     {
557       for(x=0; x<level.fieldx; x++)
558       {
559         int element = Ur[x][y];
560
561         if (element == EL_CHAR('['))
562           element = EL_CHAR_AUMLAUT;
563         else if (element == EL_CHAR('\\'))
564           element = EL_CHAR_OUMLAUT;
565         else if (element == EL_CHAR(']'))
566           element = EL_CHAR_UUMLAUT;
567         else if (element == EL_CHAR('^'))
568           element = EL_CHAR_COPYRIGHT;
569
570         Feld[x][y] = Ur[x][y] = element;
571       }
572     }
573   }
574
575   /* determine border element for this level */
576   SetBorderElement();
577 }
578
579 void LoadLevel(int level_nr)
580 {
581   char *filename = getLevelFilename(level_nr);
582
583   LoadLevelFromFilename(filename);
584 }
585
586 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
587 {
588   putFileVersion(file, level->file_version);
589   putFileVersion(file, level->game_version);
590 }
591
592 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
593 {
594   int i, x, y;
595
596   fputc(level->fieldx, file);
597   fputc(level->fieldy, file);
598
599   putFile16BitBE(file, level->time);
600   putFile16BitBE(file, level->gems_needed);
601
602   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
603     fputc(level->name[i], file);
604
605   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
606     fputc(level->score[i], file);
607
608   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
609     for(y=0; y<3; y++)
610       for(x=0; x<3; x++)
611         fputc((level->encoding_16bit_yamyam ? EL_EMPTY :
612                level->yam_content[i][x][y]),
613               file);
614   fputc(level->amoeba_speed, file);
615   fputc(level->time_magic_wall, file);
616   fputc(level->time_wheel, file);
617   fputc((level->encoding_16bit_amoeba ? EL_EMPTY : level->amoeba_content),
618         file);
619   fputc((level->double_speed ? 1 : 0), file);
620   fputc((level->gravity ? 1 : 0), file);
621   fputc((level->encoding_16bit_field ? 1 : 0), file);
622   fputc((level->em_slippery_gems ? 1 : 0), file);
623
624   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
625 }
626
627 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
628 {
629   int i;
630
631   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
632     fputc(level->author[i], file);
633 }
634
635 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
636 {
637   int x, y;
638
639   for(y=0; y<level->fieldy; y++) 
640     for(x=0; x<level->fieldx; x++) 
641       if (level->encoding_16bit_field)
642         putFile16BitBE(file, Ur[x][y]);
643       else
644         fputc(Ur[x][y], file);
645 }
646
647 #if 0
648 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
649 {
650   int i, x, y;
651
652   fputc(EL_YAMYAM, file);
653   fputc(level->num_yam_contents, file);
654   fputc(0, file);
655   fputc(0, file);
656
657   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
658     for(y=0; y<3; y++)
659       for(x=0; x<3; x++)
660         if (level->encoding_16bit_field)
661           putFile16BitBE(file, level->yam_content[i][x][y]);
662         else
663           fputc(level->yam_content[i][x][y], file);
664 }
665 #endif
666
667 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
668 {
669   int i, x, y;
670   int num_contents, content_xsize, content_ysize;
671   int content_array[MAX_ELEMENT_CONTENTS][3][3];
672
673   if (element == EL_YAMYAM)
674   {
675     num_contents = level->num_yam_contents;
676     content_xsize = 3;
677     content_ysize = 3;
678
679     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
680       for(y=0; y<3; y++)
681         for(x=0; x<3; x++)
682           content_array[i][x][y] = level->yam_content[i][x][y];
683   }
684   else if (element == EL_BD_AMOEBA)
685   {
686     num_contents = 1;
687     content_xsize = 1;
688     content_ysize = 1;
689
690     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
691       for(y=0; y<3; y++)
692         for(x=0; x<3; x++)
693           content_array[i][x][y] = EL_EMPTY;
694     content_array[0][0][0] = level->amoeba_content;
695   }
696   else
697   {
698     /* chunk header already written -- write empty chunk data */
699     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
700
701     Error(ERR_WARN, "cannot save content for element '%d'", element);
702     return;
703   }
704
705   putFile16BitBE(file, element);
706   fputc(num_contents, file);
707   fputc(content_xsize, file);
708   fputc(content_ysize, file);
709
710   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
711
712   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
713     for(y=0; y<3; y++)
714       for(x=0; x<3; x++)
715         putFile16BitBE(file, content_array[i][x][y]);
716 }
717
718 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
719                            int num_changed_custom_elements)
720 {
721   int i, check = 0;
722
723   putFile16BitBE(file, num_changed_custom_elements);
724
725   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
726   {
727     int element = EL_CUSTOM_START + i;
728
729     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
730     {
731       if (check < num_changed_custom_elements)
732       {
733         putFile16BitBE(file, element);
734         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
735       }
736
737       check++;
738     }
739   }
740
741   if (check != num_changed_custom_elements)     /* should not happen */
742     Error(ERR_WARN, "inconsistent number of custom element properties");
743 }
744
745 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
746                            int num_changed_custom_elements)
747 {
748   int i, check = 0;
749
750   putFile16BitBE(file, num_changed_custom_elements);
751
752   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
753   {
754     int element = EL_CUSTOM_START + i;
755
756     if (level->custom_element_successor[i] != EL_EMPTY_SPACE)
757     {
758       if (check < num_changed_custom_elements)
759       {
760         putFile16BitBE(file, element);
761         putFile16BitBE(file, level->custom_element_successor[i]);
762       }
763
764       check++;
765     }
766   }
767
768   if (check != num_changed_custom_elements)     /* should not happen */
769     Error(ERR_WARN, "inconsistent number of custom element successors");
770 }
771
772 void SaveLevel(int level_nr)
773 {
774   char *filename = getLevelFilename(level_nr);
775   int body_chunk_size;
776   int num_changed_custom_elements1 = 0;
777   int num_changed_custom_elements2 = 0;
778   int i, x, y;
779   FILE *file;
780
781   if (!(file = fopen(filename, MODE_WRITE)))
782   {
783     Error(ERR_WARN, "cannot save level file '%s'", filename);
784     return;
785   }
786
787   level.file_version = FILE_VERSION_ACTUAL;
788   level.game_version = GAME_VERSION_ACTUAL;
789
790   /* check level field for 16-bit elements */
791   level.encoding_16bit_field = FALSE;
792   for(y=0; y<level.fieldy; y++) 
793     for(x=0; x<level.fieldx; x++) 
794       if (Ur[x][y] > 255)
795         level.encoding_16bit_field = TRUE;
796
797   /* check yamyam content for 16-bit elements */
798   level.encoding_16bit_yamyam = FALSE;
799   for(i=0; i<level.num_yam_contents; i++)
800     for(y=0; y<3; y++)
801       for(x=0; x<3; x++)
802         if (level.yam_content[i][x][y] > 255)
803           level.encoding_16bit_yamyam = TRUE;
804
805   /* check amoeba content for 16-bit elements */
806   level.encoding_16bit_amoeba = FALSE;
807   if (level.amoeba_content > 255)
808     level.encoding_16bit_amoeba = TRUE;
809
810   /* calculate size of "BODY" chunk */
811   body_chunk_size =
812     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
813
814   /* check for non-standard custom elements and calculate "CUS1" chunk size */
815   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
816     if (Properties[EL_CUSTOM_START +i][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
817       num_changed_custom_elements1++;
818
819   /* check for non-standard custom elements and calculate "CUS2" chunk size */
820   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
821     if (level.custom_element_successor[i] != EL_EMPTY_SPACE)
822       num_changed_custom_elements2++;
823
824   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
825   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
826
827   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
828   SaveLevel_VERS(file, &level);
829
830   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
831   SaveLevel_HEAD(file, &level);
832
833   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
834   SaveLevel_AUTH(file, &level);
835
836   putFileChunkBE(file, "BODY", body_chunk_size);
837   SaveLevel_BODY(file, &level);
838
839   if (level.encoding_16bit_yamyam ||
840       level.num_yam_contents != STD_ELEMENT_CONTENTS)
841   {
842     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
843     SaveLevel_CNT2(file, &level, EL_YAMYAM);
844   }
845
846   if (level.encoding_16bit_amoeba)
847   {
848     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
849     SaveLevel_CNT2(file, &level, EL_BD_AMOEBA);
850   }
851
852   if (num_changed_custom_elements1 > 0)
853   {
854     putFileChunkBE(file, "CUS1", 2 + num_changed_custom_elements1 * 6);
855     SaveLevel_CUS1(file, &level, num_changed_custom_elements1);
856   }
857
858   if (num_changed_custom_elements2 > 0)
859   {
860     putFileChunkBE(file, "CUS2", 2 + num_changed_custom_elements2 * 4);
861     SaveLevel_CUS2(file, &level, num_changed_custom_elements2);
862   }
863
864   fclose(file);
865
866   SetFilePermissions(filename, PERMS_PRIVATE);
867 }
868
869 void DumpLevel(struct LevelInfo *level)
870 {
871   printf_line("-", 79);
872   printf("Level xxx (file version %06d, game version %06d)\n",
873          level->file_version, level->game_version);
874   printf_line("-", 79);
875
876   printf("Level Author: '%s'\n", level->author);
877   printf("Level Title:  '%s'\n", level->name);
878   printf("\n");
879   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
880   printf("\n");
881   printf("Level Time:  %d seconds\n", level->time);
882   printf("Gems needed: %d\n", level->gems_needed);
883   printf("\n");
884   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
885   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
886   printf("Time for Light:      %d seconds\n", level->time_light);
887   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
888   printf("\n");
889   printf("Amoeba Speed: %d\n", level->amoeba_speed);
890   printf("\n");
891   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
892   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
893   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
894
895   printf_line("-", 79);
896 }
897
898
899 /* ========================================================================= */
900 /* tape file functions                                                       */
901 /* ========================================================================= */
902
903 static void setTapeInfoToDefaults()
904 {
905   int i;
906
907   /* always start with reliable default values (empty tape) */
908   TapeErase();
909
910   /* default values (also for pre-1.2 tapes) with only the first player */
911   tape.player_participates[0] = TRUE;
912   for(i=1; i<MAX_PLAYERS; i++)
913     tape.player_participates[i] = FALSE;
914
915   /* at least one (default: the first) player participates in every tape */
916   tape.num_participating_players = 1;
917
918   tape.level_nr = level_nr;
919   tape.counter = 0;
920   tape.changed = FALSE;
921
922   tape.recording = FALSE;
923   tape.playing = FALSE;
924   tape.pausing = FALSE;
925 }
926
927 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
928 {
929   tape->file_version = getFileVersion(file);
930   tape->game_version = getFileVersion(file);
931
932   return chunk_size;
933 }
934
935 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
936 {
937   int i;
938
939   tape->random_seed = getFile32BitBE(file);
940   tape->date        = getFile32BitBE(file);
941   tape->length      = getFile32BitBE(file);
942
943   /* read header fields that are new since version 1.2 */
944   if (tape->file_version >= FILE_VERSION_1_2)
945   {
946     byte store_participating_players = fgetc(file);
947     int engine_version;
948
949     /* since version 1.2, tapes store which players participate in the tape */
950     tape->num_participating_players = 0;
951     for(i=0; i<MAX_PLAYERS; i++)
952     {
953       tape->player_participates[i] = FALSE;
954
955       if (store_participating_players & (1 << i))
956       {
957         tape->player_participates[i] = TRUE;
958         tape->num_participating_players++;
959       }
960     }
961
962     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
963
964     engine_version = getFileVersion(file);
965     if (engine_version > 0)
966       tape->engine_version = engine_version;
967   }
968
969   return chunk_size;
970 }
971
972 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
973 {
974   int level_identifier_size;
975   int i;
976
977   level_identifier_size = getFile16BitBE(file);
978
979   tape->level_identifier =
980     checked_realloc(tape->level_identifier, level_identifier_size);
981
982   for(i=0; i < level_identifier_size; i++)
983     tape->level_identifier[i] = fgetc(file);
984
985   tape->level_nr = getFile16BitBE(file);
986
987   chunk_size = 2 + level_identifier_size + 2;
988
989   return chunk_size;
990 }
991
992 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
993 {
994   int i, j;
995   int chunk_size_expected =
996     (tape->num_participating_players + 1) * tape->length;
997
998   if (chunk_size_expected != chunk_size)
999   {
1000     ReadUnusedBytesFromFile(file, chunk_size);
1001     return chunk_size_expected;
1002   }
1003
1004   for(i=0; i<tape->length; i++)
1005   {
1006     if (i >= MAX_TAPELEN)
1007       break;
1008
1009     for(j=0; j<MAX_PLAYERS; j++)
1010     {
1011       tape->pos[i].action[j] = MV_NO_MOVING;
1012
1013       if (tape->player_participates[j])
1014         tape->pos[i].action[j] = fgetc(file);
1015     }
1016
1017     tape->pos[i].delay = fgetc(file);
1018
1019     if (tape->file_version == FILE_VERSION_1_0)
1020     {
1021       /* eliminate possible diagonal moves in old tapes */
1022       /* this is only for backward compatibility */
1023
1024       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1025       byte action = tape->pos[i].action[0];
1026       int k, num_moves = 0;
1027
1028       for (k=0; k<4; k++)
1029       {
1030         if (action & joy_dir[k])
1031         {
1032           tape->pos[i + num_moves].action[0] = joy_dir[k];
1033           if (num_moves > 0)
1034             tape->pos[i + num_moves].delay = 0;
1035           num_moves++;
1036         }
1037       }
1038
1039       if (num_moves > 1)
1040       {
1041         num_moves--;
1042         i += num_moves;
1043         tape->length += num_moves;
1044       }
1045     }
1046     else if (tape->file_version < FILE_VERSION_2_0)
1047     {
1048       /* convert pre-2.0 tapes to new tape format */
1049
1050       if (tape->pos[i].delay > 1)
1051       {
1052         /* action part */
1053         tape->pos[i + 1] = tape->pos[i];
1054         tape->pos[i + 1].delay = 1;
1055
1056         /* delay part */
1057         for(j=0; j<MAX_PLAYERS; j++)
1058           tape->pos[i].action[j] = MV_NO_MOVING;
1059         tape->pos[i].delay--;
1060
1061         i++;
1062         tape->length++;
1063       }
1064     }
1065
1066     if (feof(file))
1067       break;
1068   }
1069
1070   if (i != tape->length)
1071     chunk_size = (tape->num_participating_players + 1) * i;
1072
1073   return chunk_size;
1074 }
1075
1076 void LoadTapeFromFilename(char *filename)
1077 {
1078   char cookie[MAX_LINE_LEN];
1079   char chunk_name[CHUNK_ID_LEN + 1];
1080   FILE *file;
1081   int chunk_size;
1082
1083   /* always start with reliable default values */
1084   setTapeInfoToDefaults();
1085
1086   if (!(file = fopen(filename, MODE_READ)))
1087     return;
1088
1089   getFileChunkBE(file, chunk_name, NULL);
1090   if (strcmp(chunk_name, "RND1") == 0)
1091   {
1092     getFile32BitBE(file);               /* not used */
1093
1094     getFileChunkBE(file, chunk_name, NULL);
1095     if (strcmp(chunk_name, "TAPE") != 0)
1096     {
1097       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1098       fclose(file);
1099       return;
1100     }
1101   }
1102   else  /* check for pre-2.0 file format with cookie string */
1103   {
1104     strcpy(cookie, chunk_name);
1105     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1106     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1107       cookie[strlen(cookie) - 1] = '\0';
1108
1109     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1110     {
1111       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1112       fclose(file);
1113       return;
1114     }
1115
1116     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1117     {
1118       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1119       fclose(file);
1120       return;
1121     }
1122
1123     /* pre-2.0 tape files have no game version, so use file version here */
1124     tape.game_version = tape.file_version;
1125   }
1126
1127   if (tape.file_version < FILE_VERSION_1_2)
1128   {
1129     /* tape files from versions before 1.2.0 without chunk structure */
1130     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1131     LoadTape_BODY(file, 2 * tape.length,  &tape);
1132   }
1133   else
1134   {
1135     static struct
1136     {
1137       char *name;
1138       int size;
1139       int (*loader)(FILE *, int, struct TapeInfo *);
1140     }
1141     chunk_info[] =
1142     {
1143       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1144       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1145       { "INFO", -1,                     LoadTape_INFO },
1146       { "BODY", -1,                     LoadTape_BODY },
1147       {  NULL,  0,                      NULL }
1148     };
1149
1150     while (getFileChunkBE(file, chunk_name, &chunk_size))
1151     {
1152       int i = 0;
1153
1154       while (chunk_info[i].name != NULL &&
1155              strcmp(chunk_name, chunk_info[i].name) != 0)
1156         i++;
1157
1158       if (chunk_info[i].name == NULL)
1159       {
1160         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1161               chunk_name, filename);
1162         ReadUnusedBytesFromFile(file, chunk_size);
1163       }
1164       else if (chunk_info[i].size != -1 &&
1165                chunk_info[i].size != chunk_size)
1166       {
1167         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1168               chunk_size, chunk_name, filename);
1169         ReadUnusedBytesFromFile(file, chunk_size);
1170       }
1171       else
1172       {
1173         /* call function to load this tape chunk */
1174         int chunk_size_expected =
1175           (chunk_info[i].loader)(file, chunk_size, &tape);
1176
1177         /* the size of some chunks cannot be checked before reading other
1178            chunks first (like "HEAD" and "BODY") that contain some header
1179            information, so check them here */
1180         if (chunk_size_expected != chunk_size)
1181         {
1182           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1183                 chunk_size, chunk_name, filename);
1184         }
1185       }
1186     }
1187   }
1188
1189   fclose(file);
1190
1191   tape.length_seconds = GetTapeLength();
1192 }
1193
1194 void LoadTape(int level_nr)
1195 {
1196   char *filename = getTapeFilename(level_nr);
1197
1198   LoadTapeFromFilename(filename);
1199 }
1200
1201 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1202 {
1203   putFileVersion(file, tape->file_version);
1204   putFileVersion(file, tape->game_version);
1205 }
1206
1207 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1208 {
1209   int i;
1210   byte store_participating_players = 0;
1211
1212   /* set bits for participating players for compact storage */
1213   for(i=0; i<MAX_PLAYERS; i++)
1214     if (tape->player_participates[i])
1215       store_participating_players |= (1 << i);
1216
1217   putFile32BitBE(file, tape->random_seed);
1218   putFile32BitBE(file, tape->date);
1219   putFile32BitBE(file, tape->length);
1220
1221   fputc(store_participating_players, file);
1222
1223   /* unused bytes not at the end here for 4-byte alignment of engine_version */
1224   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1225
1226   putFileVersion(file, tape->engine_version);
1227 }
1228
1229 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1230 {
1231   int level_identifier_size = strlen(tape->level_identifier) + 1;
1232   int i;
1233
1234   putFile16BitBE(file, level_identifier_size);
1235
1236   for(i=0; i < level_identifier_size; i++)
1237     fputc(tape->level_identifier[i], file);
1238
1239   putFile16BitBE(file, tape->level_nr);
1240 }
1241
1242 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1243 {
1244   int i, j;
1245
1246   for(i=0; i<tape->length; i++)
1247   {
1248     for(j=0; j<MAX_PLAYERS; j++)
1249       if (tape->player_participates[j])
1250         fputc(tape->pos[i].action[j], file);
1251
1252     fputc(tape->pos[i].delay, file);
1253   }
1254 }
1255
1256 void SaveTape(int level_nr)
1257 {
1258   char *filename = getTapeFilename(level_nr);
1259   FILE *file;
1260   boolean new_tape = TRUE;
1261   int num_participating_players = 0;
1262   int info_chunk_size;
1263   int body_chunk_size;
1264   int i;
1265
1266   InitTapeDirectory(leveldir_current->filename);
1267
1268   /* if a tape still exists, ask to overwrite it */
1269   if (access(filename, F_OK) == 0)
1270   {
1271     new_tape = FALSE;
1272     if (!Request("Replace old tape ?", REQ_ASK))
1273       return;
1274   }
1275
1276   if (!(file = fopen(filename, MODE_WRITE)))
1277   {
1278     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1279     return;
1280   }
1281
1282   tape.file_version = FILE_VERSION_ACTUAL;
1283   tape.game_version = GAME_VERSION_ACTUAL;
1284
1285   /* count number of participating players  */
1286   for(i=0; i<MAX_PLAYERS; i++)
1287     if (tape.player_participates[i])
1288       num_participating_players++;
1289
1290   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1291   body_chunk_size = (num_participating_players + 1) * tape.length;
1292
1293   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1294   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1295
1296   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1297   SaveTape_VERS(file, &tape);
1298
1299   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1300   SaveTape_HEAD(file, &tape);
1301
1302   putFileChunkBE(file, "INFO", info_chunk_size);
1303   SaveTape_INFO(file, &tape);
1304
1305   putFileChunkBE(file, "BODY", body_chunk_size);
1306   SaveTape_BODY(file, &tape);
1307
1308   fclose(file);
1309
1310   SetFilePermissions(filename, PERMS_PRIVATE);
1311
1312   tape.changed = FALSE;
1313
1314   if (new_tape)
1315     Request("tape saved !", REQ_CONFIRM);
1316 }
1317
1318 void DumpTape(struct TapeInfo *tape)
1319 {
1320   int i, j;
1321
1322   if (TAPE_IS_EMPTY(*tape))
1323   {
1324     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1325     return;
1326   }
1327
1328   printf_line("-", 79);
1329   printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1330          tape->level_nr, tape->file_version, tape->game_version);
1331   printf("Level series identifier: '%s'\n", tape->level_identifier);
1332   printf_line("-", 79);
1333
1334   for(i=0; i<tape->length; i++)
1335   {
1336     if (i >= MAX_TAPELEN)
1337       break;
1338
1339     printf("%03d: ", i);
1340
1341     for(j=0; j<MAX_PLAYERS; j++)
1342     {
1343       if (tape->player_participates[j])
1344       {
1345         int action = tape->pos[i].action[j];
1346
1347         printf("%d:%02x ", j, action);
1348         printf("[%c%c%c%c|%c%c] - ",
1349                (action & JOY_LEFT ? '<' : ' '),
1350                (action & JOY_RIGHT ? '>' : ' '),
1351                (action & JOY_UP ? '^' : ' '),
1352                (action & JOY_DOWN ? 'v' : ' '),
1353                (action & JOY_BUTTON_1 ? '1' : ' '),
1354                (action & JOY_BUTTON_2 ? '2' : ' '));
1355       }
1356     }
1357
1358     printf("(%03d)\n", tape->pos[i].delay);
1359   }
1360
1361   printf_line("-", 79);
1362 }
1363
1364
1365 /* ========================================================================= */
1366 /* score file functions                                                      */
1367 /* ========================================================================= */
1368
1369 void LoadScore(int level_nr)
1370 {
1371   int i;
1372   char *filename = getScoreFilename(level_nr);
1373   char cookie[MAX_LINE_LEN];
1374   char line[MAX_LINE_LEN];
1375   char *line_ptr;
1376   FILE *file;
1377
1378   /* always start with reliable default values */
1379   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1380   {
1381     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1382     highscore[i].Score = 0;
1383   }
1384
1385   if (!(file = fopen(filename, MODE_READ)))
1386     return;
1387
1388   /* check file identifier */
1389   fgets(cookie, MAX_LINE_LEN, file);
1390   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1391     cookie[strlen(cookie) - 1] = '\0';
1392
1393   if (!checkCookieString(cookie, SCORE_COOKIE))
1394   {
1395     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1396     fclose(file);
1397     return;
1398   }
1399
1400   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1401   {
1402     fscanf(file, "%d", &highscore[i].Score);
1403     fgets(line, MAX_LINE_LEN, file);
1404
1405     if (line[strlen(line) - 1] == '\n')
1406       line[strlen(line) - 1] = '\0';
1407
1408     for (line_ptr = line; *line_ptr; line_ptr++)
1409     {
1410       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1411       {
1412         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1413         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1414         break;
1415       }
1416     }
1417   }
1418
1419   fclose(file);
1420 }
1421
1422 void SaveScore(int level_nr)
1423 {
1424   int i;
1425   char *filename = getScoreFilename(level_nr);
1426   FILE *file;
1427
1428   InitScoreDirectory(leveldir_current->filename);
1429
1430   if (!(file = fopen(filename, MODE_WRITE)))
1431   {
1432     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1433     return;
1434   }
1435
1436   fprintf(file, "%s\n\n", SCORE_COOKIE);
1437
1438   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1439     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1440
1441   fclose(file);
1442
1443   SetFilePermissions(filename, PERMS_PUBLIC);
1444 }
1445
1446
1447 /* ========================================================================= */
1448 /* setup file functions                                                      */
1449 /* ========================================================================= */
1450
1451 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1452
1453 /* global setup */
1454 #define SETUP_TOKEN_PLAYER_NAME                 0
1455 #define SETUP_TOKEN_SOUND                       1
1456 #define SETUP_TOKEN_SOUND_LOOPS                 2
1457 #define SETUP_TOKEN_SOUND_MUSIC                 3
1458 #define SETUP_TOKEN_SOUND_SIMPLE                4
1459 #define SETUP_TOKEN_TOONS                       5
1460 #define SETUP_TOKEN_SCROLL_DELAY                6
1461 #define SETUP_TOKEN_SOFT_SCROLLING              7
1462 #define SETUP_TOKEN_FADING                      8
1463 #define SETUP_TOKEN_AUTORECORD                  9
1464 #define SETUP_TOKEN_QUICK_DOORS                 10
1465 #define SETUP_TOKEN_TEAM_MODE                   11
1466 #define SETUP_TOKEN_HANDICAP                    12
1467 #define SETUP_TOKEN_TIME_LIMIT                  13
1468 #define SETUP_TOKEN_FULLSCREEN                  14
1469 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1470 #define SETUP_TOKEN_GRAPHICS_SET                16
1471 #define SETUP_TOKEN_SOUNDS_SET                  17
1472 #define SETUP_TOKEN_MUSIC_SET                   18
1473 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1474 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1475 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1476
1477 #define NUM_GLOBAL_SETUP_TOKENS                 22
1478
1479 /* editor setup */
1480 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
1481 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
1482 #define SETUP_TOKEN_EDITOR_EL_MORE              2
1483 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
1484 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
1485 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
1486 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
1487 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
1488 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
1489
1490 #define NUM_EDITOR_SETUP_TOKENS                 9
1491
1492 /* shortcut setup */
1493 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
1494 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
1495 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
1496
1497 #define NUM_SHORTCUT_SETUP_TOKENS               3
1498
1499 /* player setup */
1500 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
1501 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
1502 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
1503 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
1504 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
1505 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
1506 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
1507 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
1508 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
1509 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
1510 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
1511 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
1512 #define SETUP_TOKEN_PLAYER_KEY_UP               12
1513 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
1514 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
1515 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
1516
1517 #define NUM_PLAYER_SETUP_TOKENS                 16
1518
1519 /* system setup */
1520 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
1521 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
1522
1523 #define NUM_SYSTEM_SETUP_TOKENS                 2
1524
1525 /* options setup */
1526 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
1527
1528 #define NUM_OPTIONS_SETUP_TOKENS                1
1529
1530
1531 static struct SetupInfo si;
1532 static struct SetupEditorInfo sei;
1533 static struct SetupShortcutInfo ssi;
1534 static struct SetupInputInfo sii;
1535 static struct SetupSystemInfo syi;
1536 static struct OptionInfo soi;
1537
1538 static struct TokenInfo global_setup_tokens[] =
1539 {
1540   { TYPE_STRING, &si.player_name,       "player_name"                   },
1541   { TYPE_SWITCH, &si.sound,             "sound"                         },
1542   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1543   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1544   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1545   { TYPE_SWITCH, &si.toons,             "toons"                         },
1546   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1547   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1548   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1549   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1550   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1551   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1552   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1553   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1554   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1555   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1556   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1557   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1558   { TYPE_STRING, &si.music_set,         "music_set"                     },
1559   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1560   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1561   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1562 };
1563
1564 static struct TokenInfo editor_setup_tokens[] =
1565 {
1566   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1567   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1568   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1569   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1570   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1571   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1572   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1573   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1574   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1575 };
1576
1577 static struct TokenInfo shortcut_setup_tokens[] =
1578 {
1579   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1580   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1581   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1582 };
1583
1584 static struct TokenInfo player_setup_tokens[] =
1585 {
1586   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1587   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1588   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1589   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1590   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1591   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1592   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1593   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1594   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1595   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1596   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1597   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1598   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1599   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1600   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1601   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1602 };
1603
1604 static struct TokenInfo system_setup_tokens[] =
1605 {
1606   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
1607   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1608 };
1609
1610 static struct TokenInfo options_setup_tokens[] =
1611 {
1612   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
1613 };
1614
1615 static char *get_corrected_login_name(char *login_name)
1616 {
1617   /* needed because player name must be a fixed length string */
1618   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1619
1620   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1621   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1622
1623   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
1624     if (strchr(login_name_new, ' '))
1625       *strchr(login_name_new, ' ') = '\0';
1626
1627   return login_name_new;
1628 }
1629
1630 static void setSetupInfoToDefaults(struct SetupInfo *si)
1631 {
1632   int i;
1633
1634   si->player_name = get_corrected_login_name(getLoginName());
1635
1636   si->sound = TRUE;
1637   si->sound_loops = TRUE;
1638   si->sound_music = TRUE;
1639   si->sound_simple = TRUE;
1640   si->toons = TRUE;
1641   si->double_buffering = TRUE;
1642   si->direct_draw = !si->double_buffering;
1643   si->scroll_delay = TRUE;
1644   si->soft_scrolling = TRUE;
1645   si->fading = FALSE;
1646   si->autorecord = TRUE;
1647   si->quick_doors = FALSE;
1648   si->team_mode = FALSE;
1649   si->handicap = TRUE;
1650   si->time_limit = TRUE;
1651   si->fullscreen = FALSE;
1652   si->ask_on_escape = TRUE;
1653
1654   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1655   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1656   si->music_set = getStringCopy(MUSIC_SUBDIR);
1657   si->override_level_graphics = FALSE;
1658   si->override_level_sounds = FALSE;
1659   si->override_level_music = FALSE;
1660
1661   si->editor.el_boulderdash = TRUE;
1662   si->editor.el_emerald_mine = TRUE;
1663   si->editor.el_more = TRUE;
1664   si->editor.el_sokoban = TRUE;
1665   si->editor.el_supaplex = TRUE;
1666   si->editor.el_diamond_caves = TRUE;
1667   si->editor.el_dx_boulderdash = TRUE;
1668   si->editor.el_chars = TRUE;
1669   si->editor.el_custom = TRUE;
1670
1671   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1672   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1673   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1674
1675   for (i=0; i<MAX_PLAYERS; i++)
1676   {
1677     si->input[i].use_joystick = FALSE;
1678     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1679     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1680     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1681     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1682     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1683     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1684     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1685     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1686     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1687     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1688     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1689     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1690     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1691     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1692     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1693   }
1694
1695   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1696   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1697
1698   si->options.verbose = FALSE;
1699 }
1700
1701 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1702 {
1703   int i, pnr;
1704
1705   if (!setup_file_list)
1706     return;
1707
1708   /* global setup */
1709   si = setup;
1710   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1711     setSetupInfo(global_setup_tokens, i,
1712                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1713   setup = si;
1714
1715   /* editor setup */
1716   sei = setup.editor;
1717   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1718     setSetupInfo(editor_setup_tokens, i,
1719                  getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1720   setup.editor = sei;
1721
1722   /* shortcut setup */
1723   ssi = setup.shortcut;
1724   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1725     setSetupInfo(shortcut_setup_tokens, i,
1726                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1727   setup.shortcut = ssi;
1728
1729   /* player setup */
1730   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1731   {
1732     char prefix[30];
1733
1734     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1735
1736     sii = setup.input[pnr];
1737     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1738     {
1739       char full_token[100];
1740
1741       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1742       setSetupInfo(player_setup_tokens, i,
1743                    getTokenValue(setup_file_list, full_token));
1744     }
1745     setup.input[pnr] = sii;
1746   }
1747
1748   /* system setup */
1749   syi = setup.system;
1750   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1751     setSetupInfo(system_setup_tokens, i,
1752                  getTokenValue(setup_file_list, system_setup_tokens[i].text));
1753   setup.system = syi;
1754
1755   /* options setup */
1756   soi = setup.options;
1757   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1758     setSetupInfo(options_setup_tokens, i,
1759                  getTokenValue(setup_file_list, options_setup_tokens[i].text));
1760   setup.options = soi;
1761 }
1762
1763 void LoadSetup()
1764 {
1765   char *filename = getSetupFilename();
1766   struct SetupFileList *setup_file_list = NULL;
1767
1768   /* always start with reliable default values */
1769   setSetupInfoToDefaults(&setup);
1770
1771   setup_file_list = loadSetupFileList(filename);
1772
1773   if (setup_file_list)
1774   {
1775     char *player_name_new;
1776
1777     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1778     decodeSetupFileList(setup_file_list);
1779
1780     setup.direct_draw = !setup.double_buffering;
1781
1782     freeSetupFileList(setup_file_list);
1783
1784     /* needed to work around problems with fixed length strings */
1785     player_name_new = get_corrected_login_name(setup.player_name);
1786     free(setup.player_name);
1787     setup.player_name = player_name_new;
1788   }
1789   else
1790     Error(ERR_WARN, "using default setup values");
1791 }
1792
1793 void SaveSetup()
1794 {
1795   char *filename = getSetupFilename();
1796   FILE *file;
1797   int i, pnr;
1798
1799   InitUserDataDirectory();
1800
1801   if (!(file = fopen(filename, MODE_WRITE)))
1802   {
1803     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1804     return;
1805   }
1806
1807   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1808                                                getCookie("SETUP")));
1809   fprintf(file, "\n");
1810
1811   /* global setup */
1812   si = setup;
1813   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1814   {
1815     /* just to make things nicer :) */
1816     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1817         i == SETUP_TOKEN_GRAPHICS_SET)
1818       fprintf(file, "\n");
1819
1820     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1821   }
1822
1823   /* editor setup */
1824   sei = setup.editor;
1825   fprintf(file, "\n");
1826   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1827     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1828
1829   /* shortcut setup */
1830   ssi = setup.shortcut;
1831   fprintf(file, "\n");
1832   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1833     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1834
1835   /* player setup */
1836   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1837   {
1838     char prefix[30];
1839
1840     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1841     fprintf(file, "\n");
1842
1843     sii = setup.input[pnr];
1844     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1845       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1846   }
1847
1848   /* system setup */
1849   syi = setup.system;
1850   fprintf(file, "\n");
1851   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1852     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1853
1854   /* options setup */
1855   soi = setup.options;
1856   fprintf(file, "\n");
1857   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1858     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1859
1860   fclose(file);
1861
1862   SetFilePermissions(filename, PERMS_PRIVATE);
1863 }
1864
1865 void LoadCustomElementDescriptions()
1866 {
1867   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1868   struct SetupFileList *setup_file_list;
1869   int i;
1870
1871   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1872   {
1873     if (element_info[i].custom_description != NULL)
1874     {
1875       free(element_info[i].custom_description);
1876       element_info[i].custom_description = NULL;
1877     }
1878   }
1879
1880   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1881     return;
1882
1883   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1884   {
1885     char *token = getStringCat2(element_info[i].token_name, ".name");
1886     char *value = getTokenValue(setup_file_list, token);
1887
1888     if (value != NULL)
1889       element_info[i].custom_description = getStringCopy(value);
1890
1891     free(token);
1892   }
1893
1894   freeSetupFileList(setup_file_list);
1895 }
1896
1897 void LoadSpecialMenuDesignSettings()
1898 {
1899   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1900   struct SetupFileList *setup_file_list;
1901   char *value;
1902
1903   /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1904   global.num_toons = 20;
1905   global.menu_draw_xoffset = 0;
1906   global.menu_draw_yoffset = 0;
1907   global.menu_draw_xoffset_MAIN = 0;
1908   global.menu_draw_yoffset_MAIN = 0;
1909   global.door_step_offset = 2;
1910   global.door_step_delay = 10;
1911
1912   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1913     return;
1914
1915   value = getTokenValue(setup_file_list, "global.num_toons");
1916   if (value != NULL)
1917     global.num_toons = get_integer_from_string(value);
1918
1919   value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1920   if (value != NULL)
1921     global.menu_draw_xoffset = get_integer_from_string(value);
1922
1923   value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1924   if (value != NULL)
1925     global.menu_draw_yoffset = get_integer_from_string(value);
1926
1927   value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1928   if (value != NULL)
1929     global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1930
1931   value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1932   if (value != NULL)
1933     global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1934
1935   value = getTokenValue(setup_file_list, "door.step_offset");
1936   if (value != NULL)
1937     global.door_step_offset = get_integer_from_string(value);
1938
1939   value = getTokenValue(setup_file_list, "door.step_delay");
1940   if (value != NULL)
1941     global.door_step_delay = get_integer_from_string(value);
1942
1943   freeSetupFileList(setup_file_list);
1944 }