rnd-20020803-1-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "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   TapeErase();
694
695   /* default values (also for pre-1.2 tapes) with only the first player */
696   tape.player_participates[0] = TRUE;
697   for(i=1; i<MAX_PLAYERS; i++)
698     tape.player_participates[i] = FALSE;
699
700   /* at least one (default: the first) player participates in every tape */
701   tape.num_participating_players = 1;
702
703   tape.level_nr = level_nr;
704   tape.counter = 0;
705   tape.changed = FALSE;
706
707   tape.recording = FALSE;
708   tape.playing = FALSE;
709   tape.pausing = FALSE;
710 }
711
712 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
713 {
714   tape->file_version = getFileVersion(file);
715   tape->game_version = getFileVersion(file);
716
717   return chunk_size;
718 }
719
720 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
721 {
722   int i;
723
724   tape->random_seed = getFile32BitBE(file);
725   tape->date        = getFile32BitBE(file);
726   tape->length      = getFile32BitBE(file);
727
728   /* read header fields that are new since version 1.2 */
729   if (tape->file_version >= FILE_VERSION_1_2)
730   {
731     byte store_participating_players = fgetc(file);
732     int engine_version;
733
734     /* since version 1.2, tapes store which players participate in the tape */
735     tape->num_participating_players = 0;
736     for(i=0; i<MAX_PLAYERS; i++)
737     {
738       tape->player_participates[i] = FALSE;
739
740       if (store_participating_players & (1 << i))
741       {
742         tape->player_participates[i] = TRUE;
743         tape->num_participating_players++;
744       }
745     }
746
747     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
748
749     engine_version = getFileVersion(file);
750     if (engine_version > 0)
751       tape->engine_version = engine_version;
752   }
753
754   return chunk_size;
755 }
756
757 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
758 {
759   int i, j;
760   int chunk_size_expected =
761     (tape->num_participating_players + 1) * tape->length;
762
763   if (chunk_size_expected != chunk_size)
764   {
765     ReadUnusedBytesFromFile(file, chunk_size);
766     return chunk_size_expected;
767   }
768
769   for(i=0; i<tape->length; i++)
770   {
771     if (i >= MAX_TAPELEN)
772       break;
773
774     for(j=0; j<MAX_PLAYERS; j++)
775     {
776       tape->pos[i].action[j] = MV_NO_MOVING;
777
778       if (tape->player_participates[j])
779         tape->pos[i].action[j] = fgetc(file);
780     }
781
782     tape->pos[i].delay = fgetc(file);
783
784     if (tape->file_version == FILE_VERSION_1_0)
785     {
786       /* eliminate possible diagonal moves in old tapes */
787       /* this is only for backward compatibility */
788
789       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
790       byte action = tape->pos[i].action[0];
791       int k, num_moves = 0;
792
793       for (k=0; k<4; k++)
794       {
795         if (action & joy_dir[k])
796         {
797           tape->pos[i + num_moves].action[0] = joy_dir[k];
798           if (num_moves > 0)
799             tape->pos[i + num_moves].delay = 0;
800           num_moves++;
801         }
802       }
803
804       if (num_moves > 1)
805       {
806         num_moves--;
807         i += num_moves;
808         tape->length += num_moves;
809       }
810     }
811     else if (tape->file_version < FILE_VERSION_2_0)
812     {
813       /* convert pre-2.0 tapes to new tape format */
814
815       if (tape->pos[i].delay > 1)
816       {
817         /* action part */
818         tape->pos[i + 1] = tape->pos[i];
819         tape->pos[i + 1].delay = 1;
820
821         /* delay part */
822         for(j=0; j<MAX_PLAYERS; j++)
823           tape->pos[i].action[j] = MV_NO_MOVING;
824         tape->pos[i].delay--;
825
826         i++;
827         tape->length++;
828       }
829     }
830
831     if (feof(file))
832       break;
833   }
834
835   if (i != tape->length)
836     chunk_size = (tape->num_participating_players + 1) * i;
837
838   return chunk_size;
839 }
840
841 void LoadTape(int level_nr)
842 {
843   char *filename = getTapeFilename(level_nr);
844   char cookie[MAX_LINE_LEN];
845   char chunk_name[CHUNK_ID_LEN + 1];
846   FILE *file;
847   int chunk_size;
848
849   /* always start with reliable default values */
850   setTapeInfoToDefaults();
851
852   if (!(file = fopen(filename, MODE_READ)))
853     return;
854
855   getFileChunkBE(file, chunk_name, NULL);
856   if (strcmp(chunk_name, "RND1") == 0)
857   {
858     getFile32BitBE(file);               /* not used */
859
860     getFileChunkBE(file, chunk_name, NULL);
861     if (strcmp(chunk_name, "TAPE") != 0)
862     {
863       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
864       fclose(file);
865       return;
866     }
867   }
868   else  /* check for pre-2.0 file format with cookie string */
869   {
870     strcpy(cookie, chunk_name);
871     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
872     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
873       cookie[strlen(cookie) - 1] = '\0';
874
875     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
876     {
877       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
878       fclose(file);
879       return;
880     }
881
882     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
883     {
884       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
885       fclose(file);
886       return;
887     }
888
889     /* pre-2.0 tape files have no game version, so use file version here */
890     tape.game_version = tape.file_version;
891   }
892
893   if (tape.file_version < FILE_VERSION_1_2)
894   {
895     /* tape files from versions before 1.2.0 without chunk structure */
896     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
897     LoadTape_BODY(file, 2 * tape.length,  &tape);
898   }
899   else
900   {
901     static struct
902     {
903       char *name;
904       int size;
905       int (*loader)(FILE *, int, struct TapeInfo *);
906     }
907     chunk_info[] =
908     {
909       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
910       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
911       { "BODY", -1,                     LoadTape_BODY },
912       {  NULL,  0,                      NULL }
913     };
914
915     while (getFileChunkBE(file, chunk_name, &chunk_size))
916     {
917       int i = 0;
918
919       while (chunk_info[i].name != NULL &&
920              strcmp(chunk_name, chunk_info[i].name) != 0)
921         i++;
922
923       if (chunk_info[i].name == NULL)
924       {
925         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
926               chunk_name, filename);
927         ReadUnusedBytesFromFile(file, chunk_size);
928       }
929       else if (chunk_info[i].size != -1 &&
930                chunk_info[i].size != chunk_size)
931       {
932         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
933               chunk_size, chunk_name, filename);
934         ReadUnusedBytesFromFile(file, chunk_size);
935       }
936       else
937       {
938         /* call function to load this tape chunk */
939         int chunk_size_expected =
940           (chunk_info[i].loader)(file, chunk_size, &tape);
941
942         /* the size of some chunks cannot be checked before reading other
943            chunks first (like "HEAD" and "BODY") that contain some header
944            information, so check them here */
945         if (chunk_size_expected != chunk_size)
946         {
947           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
948                 chunk_size, chunk_name, filename);
949         }
950       }
951     }
952   }
953
954   fclose(file);
955
956   tape.length_seconds = GetTapeLength();
957 }
958
959 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
960 {
961   putFileVersion(file, tape->file_version);
962   putFileVersion(file, tape->game_version);
963 }
964
965 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
966 {
967   int i;
968   byte store_participating_players = 0;
969
970   /* set bits for participating players for compact storage */
971   for(i=0; i<MAX_PLAYERS; i++)
972     if (tape->player_participates[i])
973       store_participating_players |= (1 << i);
974
975   putFile32BitBE(file, tape->random_seed);
976   putFile32BitBE(file, tape->date);
977   putFile32BitBE(file, tape->length);
978
979   fputc(store_participating_players, file);
980
981   /* unused bytes not at the end here for 4-byte alignment of engine_version */
982   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
983
984   putFileVersion(file, tape->engine_version);
985 }
986
987 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
988 {
989   int i, j;
990
991   for(i=0; i<tape->length; i++)
992   {
993     for(j=0; j<MAX_PLAYERS; j++)
994       if (tape->player_participates[j])
995         fputc(tape->pos[i].action[j], file);
996
997     fputc(tape->pos[i].delay, file);
998   }
999 }
1000
1001 void SaveTape(int level_nr)
1002 {
1003   int i;
1004   char *filename = getTapeFilename(level_nr);
1005   FILE *file;
1006   boolean new_tape = TRUE;
1007   int num_participating_players = 0;
1008   int body_chunk_size;
1009
1010   InitTapeDirectory(leveldir_current->filename);
1011
1012   /* if a tape still exists, ask to overwrite it */
1013   if (access(filename, F_OK) == 0)
1014   {
1015     new_tape = FALSE;
1016     if (!Request("Replace old tape ?", REQ_ASK))
1017       return;
1018   }
1019
1020   if (!(file = fopen(filename, MODE_WRITE)))
1021   {
1022     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1023     return;
1024   }
1025
1026   tape.file_version = FILE_VERSION_ACTUAL;
1027   tape.game_version = GAME_VERSION_ACTUAL;
1028
1029   /* count number of participating players  */
1030   for(i=0; i<MAX_PLAYERS; i++)
1031     if (tape.player_participates[i])
1032       num_participating_players++;
1033
1034   body_chunk_size = (num_participating_players + 1) * tape.length;
1035
1036   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1037   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1038
1039   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1040   SaveTape_VERS(file, &tape);
1041
1042   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1043   SaveTape_HEAD(file, &tape);
1044
1045   putFileChunkBE(file, "BODY", body_chunk_size);
1046   SaveTape_BODY(file, &tape);
1047
1048   fclose(file);
1049
1050   SetFilePermissions(filename, PERMS_PRIVATE);
1051
1052   tape.changed = FALSE;
1053
1054   if (new_tape)
1055     Request("tape saved !", REQ_CONFIRM);
1056 }
1057
1058 void DumpTape(struct TapeInfo *tape)
1059 {
1060   int i, j;
1061
1062   if (TAPE_IS_EMPTY(*tape))
1063   {
1064     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1065     return;
1066   }
1067
1068   printf("\n");
1069   printf("-------------------------------------------------------------------------------\n");
1070   printf("Tape of Level %d (file version %06d, game version %06d)\n",
1071          tape->level_nr, tape->file_version, tape->game_version);
1072   printf("-------------------------------------------------------------------------------\n");
1073
1074   for(i=0; i<tape->length; i++)
1075   {
1076     if (i >= MAX_TAPELEN)
1077       break;
1078
1079     for(j=0; j<MAX_PLAYERS; j++)
1080     {
1081       if (tape->player_participates[j])
1082       {
1083         int action = tape->pos[i].action[j];
1084
1085         printf("%d:%02x ", j, action);
1086         printf("[%c%c%c%c|%c%c] - ",
1087                (action & JOY_LEFT ? '<' : ' '),
1088                (action & JOY_RIGHT ? '>' : ' '),
1089                (action & JOY_UP ? '^' : ' '),
1090                (action & JOY_DOWN ? 'v' : ' '),
1091                (action & JOY_BUTTON_1 ? '1' : ' '),
1092                (action & JOY_BUTTON_2 ? '2' : ' '));
1093       }
1094     }
1095
1096     printf("(%03d)\n", tape->pos[i].delay);
1097   }
1098
1099   printf("-------------------------------------------------------------------------------\n");
1100 }
1101
1102
1103 /* ========================================================================= */
1104 /* score file functions                                                      */
1105 /* ========================================================================= */
1106
1107 void LoadScore(int level_nr)
1108 {
1109   int i;
1110   char *filename = getScoreFilename(level_nr);
1111   char cookie[MAX_LINE_LEN];
1112   char line[MAX_LINE_LEN];
1113   char *line_ptr;
1114   FILE *file;
1115
1116   /* always start with reliable default values */
1117   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1118   {
1119     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1120     highscore[i].Score = 0;
1121   }
1122
1123   if (!(file = fopen(filename, MODE_READ)))
1124     return;
1125
1126   /* check file identifier */
1127   fgets(cookie, MAX_LINE_LEN, file);
1128   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1129     cookie[strlen(cookie) - 1] = '\0';
1130
1131   if (!checkCookieString(cookie, SCORE_COOKIE))
1132   {
1133     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1134     fclose(file);
1135     return;
1136   }
1137
1138   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1139   {
1140     fscanf(file, "%d", &highscore[i].Score);
1141     fgets(line, MAX_LINE_LEN, file);
1142
1143     if (line[strlen(line) - 1] == '\n')
1144       line[strlen(line) - 1] = '\0';
1145
1146     for (line_ptr = line; *line_ptr; line_ptr++)
1147     {
1148       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1149       {
1150         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1151         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1152         break;
1153       }
1154     }
1155   }
1156
1157   fclose(file);
1158 }
1159
1160 void SaveScore(int level_nr)
1161 {
1162   int i;
1163   char *filename = getScoreFilename(level_nr);
1164   FILE *file;
1165
1166   InitScoreDirectory(leveldir_current->filename);
1167
1168   if (!(file = fopen(filename, MODE_WRITE)))
1169   {
1170     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1171     return;
1172   }
1173
1174   fprintf(file, "%s\n\n", SCORE_COOKIE);
1175
1176   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1177     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1178
1179   fclose(file);
1180
1181   SetFilePermissions(filename, PERMS_PUBLIC);
1182 }
1183
1184
1185 /* ========================================================================= */
1186 /* setup file functions                                                      */
1187 /* ========================================================================= */
1188
1189 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1190
1191 /* global setup */
1192 #define SETUP_TOKEN_PLAYER_NAME                 0
1193 #define SETUP_TOKEN_SOUND                       1
1194 #define SETUP_TOKEN_SOUND_LOOPS                 2
1195 #define SETUP_TOKEN_SOUND_MUSIC                 3
1196 #define SETUP_TOKEN_SOUND_SIMPLE                4
1197 #define SETUP_TOKEN_TOONS                       5
1198 #define SETUP_TOKEN_SCROLL_DELAY                6
1199 #define SETUP_TOKEN_SOFT_SCROLLING              7
1200 #define SETUP_TOKEN_FADING                      8
1201 #define SETUP_TOKEN_AUTORECORD                  9
1202 #define SETUP_TOKEN_QUICK_DOORS                 10
1203 #define SETUP_TOKEN_TEAM_MODE                   11
1204 #define SETUP_TOKEN_HANDICAP                    12
1205 #define SETUP_TOKEN_TIME_LIMIT                  13
1206 #define SETUP_TOKEN_FULLSCREEN                  14
1207 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1208 #define SETUP_TOKEN_GRAPHICS_SET                16
1209 #define SETUP_TOKEN_SOUNDS_SET                  17
1210 #define SETUP_TOKEN_MUSIC_SET                   18
1211 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1212 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1213 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1214
1215 #define NUM_GLOBAL_SETUP_TOKENS                 22
1216
1217 /* shortcut setup */
1218 #define SETUP_TOKEN_SAVE_GAME                   0
1219 #define SETUP_TOKEN_LOAD_GAME                   1
1220 #define SETUP_TOKEN_TOGGLE_PAUSE                2
1221
1222 #define NUM_SHORTCUT_SETUP_TOKENS               3
1223
1224 /* player setup */
1225 #define SETUP_TOKEN_USE_JOYSTICK                0
1226 #define SETUP_TOKEN_JOY_DEVICE_NAME             1
1227 #define SETUP_TOKEN_JOY_XLEFT                   2
1228 #define SETUP_TOKEN_JOY_XMIDDLE                 3
1229 #define SETUP_TOKEN_JOY_XRIGHT                  4
1230 #define SETUP_TOKEN_JOY_YUPPER                  5
1231 #define SETUP_TOKEN_JOY_YMIDDLE                 6
1232 #define SETUP_TOKEN_JOY_YLOWER                  7
1233 #define SETUP_TOKEN_JOY_SNAP                    8
1234 #define SETUP_TOKEN_JOY_BOMB                    9
1235 #define SETUP_TOKEN_KEY_LEFT                    10
1236 #define SETUP_TOKEN_KEY_RIGHT                   11
1237 #define SETUP_TOKEN_KEY_UP                      12
1238 #define SETUP_TOKEN_KEY_DOWN                    13
1239 #define SETUP_TOKEN_KEY_SNAP                    14
1240 #define SETUP_TOKEN_KEY_BOMB                    15
1241
1242 #define NUM_PLAYER_SETUP_TOKENS                 16
1243
1244 static struct SetupInfo si;
1245 static struct SetupShortcutInfo ssi;
1246 static struct SetupInputInfo sii;
1247
1248 static struct TokenInfo global_setup_tokens[] =
1249 {
1250   /* global setup */
1251   { TYPE_STRING, &si.player_name,       "player_name"                   },
1252   { TYPE_SWITCH, &si.sound,             "sound"                         },
1253   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1254   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1255   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1256   { TYPE_SWITCH, &si.toons,             "toons"                         },
1257   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1258   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1259   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1260   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1261   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1262   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1263   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1264   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1265   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1266   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1267   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1268   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1269   { TYPE_STRING, &si.music_set,         "music_set"                     },
1270   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1271   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1272   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1273 };
1274
1275 static struct TokenInfo shortcut_setup_tokens[] =
1276 {
1277   /* shortcut setup */
1278   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1279   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1280   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1281 };
1282
1283 static struct TokenInfo player_setup_tokens[] =
1284 {
1285   /* player setup */
1286   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1287   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1288   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1289   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1290   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1291   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1292   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1293   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1294   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1295   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1296   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1297   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1298   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1299   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1300   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1301   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1302 };
1303
1304 static void setSetupInfoToDefaults(struct SetupInfo *si)
1305 {
1306   int i;
1307
1308   si->player_name = getStringCopy(getLoginName());
1309
1310   si->sound = TRUE;
1311   si->sound_loops = TRUE;
1312   si->sound_music = TRUE;
1313   si->sound_simple = TRUE;
1314   si->toons = TRUE;
1315   si->double_buffering = TRUE;
1316   si->direct_draw = !si->double_buffering;
1317   si->scroll_delay = TRUE;
1318   si->soft_scrolling = TRUE;
1319   si->fading = FALSE;
1320   si->autorecord = TRUE;
1321   si->quick_doors = FALSE;
1322   si->team_mode = FALSE;
1323   si->handicap = TRUE;
1324   si->time_limit = TRUE;
1325   si->fullscreen = FALSE;
1326   si->ask_on_escape = TRUE;
1327
1328   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1329   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1330   si->music_set = getStringCopy(MUSIC_SUBDIR);
1331   si->override_level_graphics = FALSE;
1332   si->override_level_sounds = FALSE;
1333   si->override_level_music = FALSE;
1334
1335   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1336   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1337   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1338
1339   for (i=0; i<MAX_PLAYERS; i++)
1340   {
1341     si->input[i].use_joystick = FALSE;
1342     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1343     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1344     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1345     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1346     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1347     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1348     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1349     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1350     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1351     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1352     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1353     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1354     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1355     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1356     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1357   }
1358 }
1359
1360 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1361 {
1362   int i, pnr;
1363
1364   if (!setup_file_list)
1365     return;
1366
1367   /* global setup */
1368   si = setup;
1369   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1370     setSetupInfo(global_setup_tokens, i,
1371                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1372   setup = si;
1373
1374   /* shortcut setup */
1375   ssi = setup.shortcut;
1376   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1377     setSetupInfo(shortcut_setup_tokens, i,
1378                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1379   setup.shortcut = ssi;
1380
1381   /* player setup */
1382   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1383   {
1384     char prefix[30];
1385
1386     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1387
1388     sii = setup.input[pnr];
1389     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1390     {
1391       char full_token[100];
1392
1393       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1394       setSetupInfo(player_setup_tokens, i,
1395                    getTokenValue(setup_file_list, full_token));
1396     }
1397     setup.input[pnr] = sii;
1398   }
1399 }
1400
1401 void LoadSetup()
1402 {
1403   char *filename = getSetupFilename();
1404   struct SetupFileList *setup_file_list = NULL;
1405
1406   /* always start with reliable default values */
1407   setSetupInfoToDefaults(&setup);
1408
1409   setup_file_list = loadSetupFileList(filename);
1410
1411   if (setup_file_list)
1412   {
1413     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1414     decodeSetupFileList(setup_file_list);
1415
1416     setup.direct_draw = !setup.double_buffering;
1417
1418     freeSetupFileList(setup_file_list);
1419
1420     /* needed to work around problems with fixed length strings */
1421     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1422       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1423     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1424     {
1425       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1426
1427       strcpy(new_name, setup.player_name);
1428       free(setup.player_name);
1429       setup.player_name = new_name;
1430     }
1431   }
1432   else
1433     Error(ERR_WARN, "using default setup values");
1434 }
1435
1436 void SaveSetup()
1437 {
1438   char *filename = getSetupFilename();
1439   FILE *file;
1440   int i, pnr;
1441
1442   InitUserDataDirectory();
1443
1444   if (!(file = fopen(filename, MODE_WRITE)))
1445   {
1446     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1447     return;
1448   }
1449
1450   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1451                                                getCookie("SETUP")));
1452   fprintf(file, "\n");
1453
1454   /* global setup */
1455   si = setup;
1456   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1457   {
1458     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1459
1460     /* just to make things nicer :) */
1461     if (i == SETUP_TOKEN_PLAYER_NAME || i == SETUP_TOKEN_GRAPHICS_SET - 1)
1462       fprintf(file, "\n");
1463   }
1464
1465   /* shortcut setup */
1466   ssi = setup.shortcut;
1467   fprintf(file, "\n");
1468   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1469     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1470
1471   /* player setup */
1472   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1473   {
1474     char prefix[30];
1475
1476     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1477     fprintf(file, "\n");
1478
1479     sii = setup.input[pnr];
1480     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1481       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1482   }
1483
1484   fclose(file);
1485
1486   SetFilePermissions(filename, PERMS_PRIVATE);
1487 }