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