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