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