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