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