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