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