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