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