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