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