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