b3557c182e235d2e41127d1f95520771dbc5b1fa
[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     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
449     if (level.game_version == VERSION_IDENT(2,0,1))
450       level.em_slippery_gems = TRUE;
451   }
452   else
453   {
454     /* Always use the latest version of the game engine for all but
455        user contributed and private levels; this allows for actual
456        corrections in the game engine to take effect for existing,
457        converted levels (from "classic" or other existing games) to
458        make the game emulation more accurate, while (hopefully) not
459        breaking existing levels created from other players. */
460
461     level.game_version = GAME_VERSION_ACTUAL;
462
463     /* Set special EM style gems behaviour: EM style gems slip down from
464        normal, steel and growing wall. As this is a more fundamental change,
465        it seems better to set the default behaviour to "off" (as it is more
466        natural) and make it configurable in the level editor (as a property
467        of gem style elements). Already existing converted levels (neither
468        private nor contributed levels) are changed to the new behaviour. */
469
470     if (level.file_version < FILE_VERSION_2_0)
471       level.em_slippery_gems = TRUE;
472   }
473
474   /* determine border element for this level */
475   SetBorderElement();
476 }
477
478 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
479 {
480   int i, x, y;
481
482   fputc(level->fieldx, file);
483   fputc(level->fieldy, file);
484
485   putFile16BitBE(file, level->time);
486   putFile16BitBE(file, level->gems_needed);
487
488   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
489     fputc(level->name[i], file);
490
491   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
492     fputc(level->score[i], file);
493
494   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
495     for(y=0; y<3; y++)
496       for(x=0; x<3; x++)
497         fputc((level->encoding_16bit_yamyam ? EL_LEERRAUM :
498                level->yam_content[i][x][y]),
499               file);
500   fputc(level->amoeba_speed, file);
501   fputc(level->time_magic_wall, file);
502   fputc(level->time_wheel, file);
503   fputc((level->encoding_16bit_amoeba ? EL_LEERRAUM : level->amoeba_content),
504         file);
505   fputc((level->double_speed ? 1 : 0), file);
506   fputc((level->gravity ? 1 : 0), file);
507   fputc((level->encoding_16bit_field ? 1 : 0), file);
508   fputc((level->em_slippery_gems ? 1 : 0), file);
509
510   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
511 }
512
513 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
514 {
515   int i;
516
517   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
518     fputc(level->author[i], file);
519 }
520
521 #if 0
522 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
523 {
524   int i, x, y;
525
526   fputc(EL_MAMPFER, file);
527   fputc(level->num_yam_contents, file);
528   fputc(0, file);
529   fputc(0, file);
530
531   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
532     for(y=0; y<3; y++)
533       for(x=0; x<3; x++)
534         if (level->encoding_16bit_field)
535           putFile16BitBE(file, level->yam_content[i][x][y]);
536         else
537           fputc(level->yam_content[i][x][y], file);
538 }
539 #endif
540
541 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
542 {
543   int x, y;
544
545   for(y=0; y<level->fieldy; y++) 
546     for(x=0; x<level->fieldx; x++) 
547       if (level->encoding_16bit_field)
548         putFile16BitBE(file, Ur[x][y]);
549       else
550         fputc(Ur[x][y], file);
551 }
552
553 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
554 {
555   int i, x, y;
556   int num_contents, content_xsize, content_ysize;
557   int content_array[MAX_ELEMENT_CONTENTS][3][3];
558
559   if (element == EL_MAMPFER)
560   {
561     num_contents = level->num_yam_contents;
562     content_xsize = 3;
563     content_ysize = 3;
564
565     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
566       for(y=0; y<3; y++)
567         for(x=0; x<3; x++)
568           content_array[i][x][y] = level->yam_content[i][x][y];
569   }
570   else if (element == EL_AMOEBE_BD)
571   {
572     num_contents = 1;
573     content_xsize = 1;
574     content_ysize = 1;
575
576     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
577       for(y=0; y<3; y++)
578         for(x=0; x<3; x++)
579           content_array[i][x][y] = EL_LEERRAUM;
580     content_array[0][0][0] = level->amoeba_content;
581   }
582   else
583   {
584     /* chunk header already written -- write empty chunk data */
585     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
586
587     Error(ERR_WARN, "cannot save content for element '%d'", element);
588     return;
589   }
590
591   putFile16BitBE(file, element);
592   fputc(num_contents, file);
593   fputc(content_xsize, file);
594   fputc(content_ysize, file);
595
596   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
597
598   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
599     for(y=0; y<3; y++)
600       for(x=0; x<3; x++)
601         putFile16BitBE(file, content_array[i][x][y]);
602 }
603
604 void SaveLevel(int level_nr)
605 {
606   int i, x, y;
607   char *filename = getLevelFilename(level_nr);
608   int body_chunk_size;
609   FILE *file;
610
611   if (!(file = fopen(filename, MODE_WRITE)))
612   {
613     Error(ERR_WARN, "cannot save level file '%s'", filename);
614     return;
615   }
616
617
618   /* check level field for 16-bit elements */
619   level.encoding_16bit_field = FALSE;
620   for(y=0; y<level.fieldy; y++) 
621     for(x=0; x<level.fieldx; x++) 
622       if (Ur[x][y] > 255)
623         level.encoding_16bit_field = TRUE;
624
625   /* check yamyam content for 16-bit elements */
626   level.encoding_16bit_yamyam = FALSE;
627   for(i=0; i<level.num_yam_contents; i++)
628     for(y=0; y<3; y++)
629       for(x=0; x<3; x++)
630         if (level.yam_content[i][x][y] > 255)
631           level.encoding_16bit_yamyam = TRUE;
632
633   /* check amoeba content for 16-bit elements */
634   level.encoding_16bit_amoeba = FALSE;
635   if (level.amoeba_content > 255)
636     level.encoding_16bit_amoeba = TRUE;
637
638   body_chunk_size =
639     level.fieldx * level.fieldy * (level.encoding_16bit_field ? 2 : 1);
640
641   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
642   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
643
644   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
645   WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
646
647   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
648   SaveLevel_HEAD(file, &level);
649
650   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
651   SaveLevel_AUTH(file, &level);
652
653   putFileChunkBE(file, "BODY", body_chunk_size);
654   SaveLevel_BODY(file, &level);
655
656   if (level.encoding_16bit_yamyam ||
657       level.num_yam_contents != STD_ELEMENT_CONTENTS)
658   {
659     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
660     SaveLevel_CNT2(file, &level, EL_MAMPFER);
661   }
662
663   if (level.encoding_16bit_amoeba)
664   {
665     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
666     SaveLevel_CNT2(file, &level, EL_AMOEBE_BD);
667   }
668
669   fclose(file);
670
671   SetFilePermissions(filename, PERMS_PRIVATE);
672 }
673
674
675 /* ========================================================================= */
676 /* tape file functions                                                       */
677 /* ========================================================================= */
678
679 static void setTapeInfoToDefaults()
680 {
681   int i;
682
683   /* always start with reliable default values (empty tape) */
684   tape.file_version = FILE_VERSION_ACTUAL;
685   tape.game_version = GAME_VERSION_ACTUAL;
686   TapeErase();
687
688   /* default values (also for pre-1.2 tapes) with only the first player */
689   tape.player_participates[0] = TRUE;
690   for(i=1; i<MAX_PLAYERS; i++)
691     tape.player_participates[i] = FALSE;
692
693   /* at least one (default: the first) player participates in every tape */
694   tape.num_participating_players = 1;
695
696   tape.level_nr = level_nr;
697   tape.counter = 0;
698   tape.changed = FALSE;
699
700   tape.recording = FALSE;
701   tape.playing = FALSE;
702   tape.pausing = FALSE;
703 }
704
705 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
706 {
707   ReadChunk_VERS(file, &(tape->file_version), &(tape->game_version));
708
709   return chunk_size;
710 }
711
712 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
713 {
714   int i;
715
716   tape->random_seed = getFile32BitBE(file);
717   tape->date        = getFile32BitBE(file);
718   tape->length      = getFile32BitBE(file);
719
720   /* read header fields that are new since version 1.2 */
721   if (tape->file_version >= FILE_VERSION_1_2)
722   {
723     byte store_participating_players = fgetc(file);
724
725     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
726
727     /* since version 1.2, tapes store which players participate in the tape */
728     tape->num_participating_players = 0;
729     for(i=0; i<MAX_PLAYERS; i++)
730     {
731       tape->player_participates[i] = FALSE;
732
733       if (store_participating_players & (1 << i))
734       {
735         tape->player_participates[i] = TRUE;
736         tape->num_participating_players++;
737       }
738     }
739   }
740
741   return chunk_size;
742 }
743
744 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
745 {
746   int i, j;
747   int chunk_size_expected =
748     (tape->num_participating_players + 1) * tape->length;
749
750   if (chunk_size_expected != chunk_size)
751   {
752     ReadUnusedBytesFromFile(file, chunk_size);
753     return chunk_size_expected;
754   }
755
756   for(i=0; i<tape->length; i++)
757   {
758     if (i >= MAX_TAPELEN)
759       break;
760
761     for(j=0; j<MAX_PLAYERS; j++)
762     {
763       tape->pos[i].action[j] = MV_NO_MOVING;
764
765       if (tape->player_participates[j])
766         tape->pos[i].action[j] = fgetc(file);
767     }
768
769     tape->pos[i].delay = fgetc(file);
770
771     if (tape->file_version == FILE_VERSION_1_0)
772     {
773       /* eliminate possible diagonal moves in old tapes */
774       /* this is only for backward compatibility */
775
776       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
777       byte action = tape->pos[i].action[0];
778       int k, num_moves = 0;
779
780       for (k=0; k<4; k++)
781       {
782         if (action & joy_dir[k])
783         {
784           tape->pos[i + num_moves].action[0] = joy_dir[k];
785           if (num_moves > 0)
786             tape->pos[i + num_moves].delay = 0;
787           num_moves++;
788         }
789       }
790
791       if (num_moves > 1)
792       {
793         num_moves--;
794         i += num_moves;
795         tape->length += num_moves;
796       }
797     }
798     else if (tape->file_version < FILE_VERSION_2_0)
799     {
800       /* convert pre-2.0 tapes to new tape format */
801
802       if (tape->pos[i].delay > 1)
803       {
804         /* action part */
805         tape->pos[i + 1] = tape->pos[i];
806         tape->pos[i + 1].delay = 1;
807
808         /* delay part */
809         for(j=0; j<MAX_PLAYERS; j++)
810           tape->pos[i].action[j] = MV_NO_MOVING;
811         tape->pos[i].delay--;
812
813         i++;
814         tape->length++;
815       }
816     }
817
818     if (feof(file))
819       break;
820   }
821
822   if (i != tape->length)
823     chunk_size = (tape->num_participating_players + 1) * i;
824
825   return chunk_size;
826 }
827
828 void LoadTape(int level_nr)
829 {
830   char *filename = getTapeFilename(level_nr);
831   char cookie[MAX_LINE_LEN];
832   char chunk_name[CHUNK_ID_LEN + 1];
833   FILE *file;
834   int chunk_size;
835
836   /* always start with reliable default values */
837   setTapeInfoToDefaults();
838
839   if (!(file = fopen(filename, MODE_READ)))
840     return;
841
842   getFileChunkBE(file, chunk_name, NULL);
843   if (strcmp(chunk_name, "RND1") == 0)
844   {
845     getFile32BitBE(file);               /* not used */
846
847     getFileChunkBE(file, chunk_name, NULL);
848     if (strcmp(chunk_name, "TAPE") != 0)
849     {
850       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
851       fclose(file);
852       return;
853     }
854   }
855   else  /* check for pre-2.0 file format with cookie string */
856   {
857     strcpy(cookie, chunk_name);
858     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
859     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
860       cookie[strlen(cookie) - 1] = '\0';
861
862     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
863     {
864       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
865       fclose(file);
866       return;
867     }
868
869     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
870     {
871       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
872       fclose(file);
873       return;
874     }
875
876     /* pre-2.0 tape files have no game version, so use file version here */
877     tape.game_version = tape.file_version;
878   }
879
880   if (tape.file_version < FILE_VERSION_1_2)
881   {
882     /* tape files from versions before 1.2.0 without chunk structure */
883     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
884     LoadTape_BODY(file, 2 * tape.length,  &tape);
885   }
886   else
887   {
888     static struct
889     {
890       char *name;
891       int size;
892       int (*loader)(FILE *, int, struct TapeInfo *);
893     }
894     chunk_info[] =
895     {
896       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
897       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
898       { "BODY", -1,                     LoadTape_BODY },
899       {  NULL,  0,                      NULL }
900     };
901
902     while (getFileChunkBE(file, chunk_name, &chunk_size))
903     {
904       int i = 0;
905
906       while (chunk_info[i].name != NULL &&
907              strcmp(chunk_name, chunk_info[i].name) != 0)
908         i++;
909
910       if (chunk_info[i].name == NULL)
911       {
912         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
913               chunk_name, filename);
914         ReadUnusedBytesFromFile(file, chunk_size);
915       }
916       else if (chunk_info[i].size != -1 &&
917                chunk_info[i].size != chunk_size)
918       {
919         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
920               chunk_size, chunk_name, filename);
921         ReadUnusedBytesFromFile(file, chunk_size);
922       }
923       else
924       {
925         /* call function to load this tape chunk */
926         int chunk_size_expected =
927           (chunk_info[i].loader)(file, chunk_size, &tape);
928
929         /* the size of some chunks cannot be checked before reading other
930            chunks first (like "HEAD" and "BODY") that contain some header
931            information, so check them here */
932         if (chunk_size_expected != chunk_size)
933         {
934           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
935                 chunk_size, chunk_name, filename);
936         }
937       }
938     }
939   }
940
941   fclose(file);
942
943   tape.length_seconds = GetTapeLength();
944 }
945
946 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
947 {
948   int i;
949   byte store_participating_players = 0;
950
951   /* set bits for participating players for compact storage */
952   for(i=0; i<MAX_PLAYERS; i++)
953     if (tape->player_participates[i])
954       store_participating_players |= (1 << i);
955
956   putFile32BitBE(file, tape->random_seed);
957   putFile32BitBE(file, tape->date);
958   putFile32BitBE(file, tape->length);
959
960   fputc(store_participating_players, file);
961
962   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
963 }
964
965 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
966 {
967   int i, j;
968
969   for(i=0; i<tape->length; i++)
970   {
971     for(j=0; j<MAX_PLAYERS; j++)
972       if (tape->player_participates[j])
973         fputc(tape->pos[i].action[j], file);
974
975     fputc(tape->pos[i].delay, file);
976   }
977 }
978
979 void SaveTape(int level_nr)
980 {
981   int i;
982   char *filename = getTapeFilename(level_nr);
983   FILE *file;
984   boolean new_tape = TRUE;
985   int num_participating_players = 0;
986   int body_chunk_size;
987
988   InitTapeDirectory(leveldir_current->filename);
989
990   /* if a tape still exists, ask to overwrite it */
991   if (access(filename, F_OK) == 0)
992   {
993     new_tape = FALSE;
994     if (!Request("Replace old tape ?", REQ_ASK))
995       return;
996   }
997
998   if (!(file = fopen(filename, MODE_WRITE)))
999   {
1000     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1001     return;
1002   }
1003
1004   /* count number of participating players  */
1005   for(i=0; i<MAX_PLAYERS; i++)
1006     if (tape.player_participates[i])
1007       num_participating_players++;
1008
1009   body_chunk_size = (num_participating_players + 1) * tape.length;
1010
1011   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1012   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1013
1014   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1015   WriteChunk_VERS(file, FILE_VERSION_ACTUAL, GAME_VERSION_ACTUAL);
1016
1017   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1018   SaveTape_HEAD(file, &tape);
1019
1020   putFileChunkBE(file, "BODY", body_chunk_size);
1021   SaveTape_BODY(file, &tape);
1022
1023   fclose(file);
1024
1025   SetFilePermissions(filename, PERMS_PRIVATE);
1026
1027   tape.changed = FALSE;
1028
1029   if (new_tape)
1030     Request("tape saved !", REQ_CONFIRM);
1031 }
1032
1033 void DumpTape(struct TapeInfo *tape)
1034 {
1035   int i, j;
1036
1037   if (TAPE_IS_EMPTY(*tape))
1038   {
1039     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1040     return;
1041   }
1042
1043   printf("\n");
1044   printf("-------------------------------------------------------------------------------\n");
1045   printf("Tape of Level %d (file version %06d, game version %06d\n",
1046          tape->level_nr, tape->file_version, tape->game_version);
1047   printf("-------------------------------------------------------------------------------\n");
1048
1049   for(i=0; i<tape->length; i++)
1050   {
1051     if (i >= MAX_TAPELEN)
1052       break;
1053
1054     for(j=0; j<MAX_PLAYERS; j++)
1055     {
1056       if (tape->player_participates[j])
1057       {
1058         int action = tape->pos[i].action[j];
1059
1060         printf("%d:%02x ", j, action);
1061         printf("[%c%c%c%c|%c%c] - ",
1062                (action & JOY_LEFT ? '<' : ' '),
1063                (action & JOY_RIGHT ? '>' : ' '),
1064                (action & JOY_UP ? '^' : ' '),
1065                (action & JOY_DOWN ? 'v' : ' '),
1066                (action & JOY_BUTTON_1 ? '1' : ' '),
1067                (action & JOY_BUTTON_2 ? '2' : ' '));
1068       }
1069     }
1070
1071     printf("(%03d)\n", tape->pos[i].delay);
1072   }
1073
1074   printf("-------------------------------------------------------------------------------\n");
1075 }
1076
1077
1078 /* ========================================================================= */
1079 /* score file functions                                                      */
1080 /* ========================================================================= */
1081
1082 void LoadScore(int level_nr)
1083 {
1084   int i;
1085   char *filename = getScoreFilename(level_nr);
1086   char cookie[MAX_LINE_LEN];
1087   char line[MAX_LINE_LEN];
1088   char *line_ptr;
1089   FILE *file;
1090
1091   /* always start with reliable default values */
1092   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1093   {
1094     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1095     highscore[i].Score = 0;
1096   }
1097
1098   if (!(file = fopen(filename, MODE_READ)))
1099     return;
1100
1101   /* check file identifier */
1102   fgets(cookie, MAX_LINE_LEN, file);
1103   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1104     cookie[strlen(cookie) - 1] = '\0';
1105
1106   if (!checkCookieString(cookie, SCORE_COOKIE))
1107   {
1108     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1109     fclose(file);
1110     return;
1111   }
1112
1113   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1114   {
1115     fscanf(file, "%d", &highscore[i].Score);
1116     fgets(line, MAX_LINE_LEN, file);
1117
1118     if (line[strlen(line) - 1] == '\n')
1119       line[strlen(line) - 1] = '\0';
1120
1121     for (line_ptr = line; *line_ptr; line_ptr++)
1122     {
1123       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1124       {
1125         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1126         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1127         break;
1128       }
1129     }
1130   }
1131
1132   fclose(file);
1133 }
1134
1135 void SaveScore(int level_nr)
1136 {
1137   int i;
1138   char *filename = getScoreFilename(level_nr);
1139   FILE *file;
1140
1141   InitScoreDirectory(leveldir_current->filename);
1142
1143   if (!(file = fopen(filename, MODE_WRITE)))
1144   {
1145     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1146     return;
1147   }
1148
1149   fprintf(file, "%s\n\n", SCORE_COOKIE);
1150
1151   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1152     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1153
1154   fclose(file);
1155
1156   SetFilePermissions(filename, PERMS_PUBLIC);
1157 }
1158
1159
1160 /* ========================================================================= */
1161 /* setup file functions                                                      */
1162 /* ========================================================================= */
1163
1164 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1165
1166 /* global setup */
1167 #define SETUP_TOKEN_PLAYER_NAME                 0
1168 #define SETUP_TOKEN_SOUND                       1
1169 #define SETUP_TOKEN_SOUND_LOOPS                 2
1170 #define SETUP_TOKEN_SOUND_MUSIC                 3
1171 #define SETUP_TOKEN_SOUND_SIMPLE                4
1172 #define SETUP_TOKEN_TOONS                       5
1173 #define SETUP_TOKEN_SCROLL_DELAY                6
1174 #define SETUP_TOKEN_SOFT_SCROLLING              7
1175 #define SETUP_TOKEN_FADING                      8
1176 #define SETUP_TOKEN_AUTORECORD                  9
1177 #define SETUP_TOKEN_QUICK_DOORS                 10
1178 #define SETUP_TOKEN_TEAM_MODE                   11
1179 #define SETUP_TOKEN_HANDICAP                    12
1180 #define SETUP_TOKEN_TIME_LIMIT                  13
1181 #define SETUP_TOKEN_FULLSCREEN                  14
1182 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1183 #define SETUP_TOKEN_GRAPHICS_SET                16
1184 #define SETUP_TOKEN_SOUNDS_SET                  17
1185 #define SETUP_TOKEN_MUSIC_SET                   18
1186 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1187 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1188 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1189
1190 #define NUM_GLOBAL_SETUP_TOKENS                 22
1191
1192 /* shortcut setup */
1193 #define SETUP_TOKEN_SAVE_GAME                   0
1194 #define SETUP_TOKEN_LOAD_GAME                   1
1195 #define SETUP_TOKEN_TOGGLE_PAUSE                2
1196
1197 #define NUM_SHORTCUT_SETUP_TOKENS               3
1198
1199 /* player setup */
1200 #define SETUP_TOKEN_USE_JOYSTICK                0
1201 #define SETUP_TOKEN_JOY_DEVICE_NAME             1
1202 #define SETUP_TOKEN_JOY_XLEFT                   2
1203 #define SETUP_TOKEN_JOY_XMIDDLE                 3
1204 #define SETUP_TOKEN_JOY_XRIGHT                  4
1205 #define SETUP_TOKEN_JOY_YUPPER                  5
1206 #define SETUP_TOKEN_JOY_YMIDDLE                 6
1207 #define SETUP_TOKEN_JOY_YLOWER                  7
1208 #define SETUP_TOKEN_JOY_SNAP                    8
1209 #define SETUP_TOKEN_JOY_BOMB                    9
1210 #define SETUP_TOKEN_KEY_LEFT                    10
1211 #define SETUP_TOKEN_KEY_RIGHT                   11
1212 #define SETUP_TOKEN_KEY_UP                      12
1213 #define SETUP_TOKEN_KEY_DOWN                    13
1214 #define SETUP_TOKEN_KEY_SNAP                    14
1215 #define SETUP_TOKEN_KEY_BOMB                    15
1216
1217 #define NUM_PLAYER_SETUP_TOKENS                 16
1218
1219 static struct SetupInfo si;
1220 static struct SetupShortcutInfo ssi;
1221 static struct SetupInputInfo sii;
1222
1223 static struct TokenInfo global_setup_tokens[] =
1224 {
1225   /* global setup */
1226   { TYPE_STRING, &si.player_name,       "player_name"                   },
1227   { TYPE_SWITCH, &si.sound,             "sound"                         },
1228   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1229   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1230   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1231   { TYPE_SWITCH, &si.toons,             "toons"                         },
1232   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1233   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1234   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1235   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1236   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1237   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1238   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1239   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1240   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1241   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1242   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1243   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1244   { TYPE_STRING, &si.music_set,         "music_set"                     },
1245   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1246   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1247   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1248 };
1249
1250 static struct TokenInfo shortcut_setup_tokens[] =
1251 {
1252   /* shortcut setup */
1253   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1254   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1255   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1256 };
1257
1258 static struct TokenInfo player_setup_tokens[] =
1259 {
1260   /* player setup */
1261   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1262   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1263   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1264   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1265   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1266   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1267   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1268   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1269   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1270   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1271   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1272   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1273   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1274   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1275   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1276   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1277 };
1278
1279 static void setSetupInfoToDefaults(struct SetupInfo *si)
1280 {
1281   int i;
1282
1283   si->player_name = getStringCopy(getLoginName());
1284
1285   si->sound = TRUE;
1286   si->sound_loops = TRUE;
1287   si->sound_music = TRUE;
1288   si->sound_simple = TRUE;
1289   si->toons = TRUE;
1290   si->double_buffering = TRUE;
1291   si->direct_draw = !si->double_buffering;
1292   si->scroll_delay = TRUE;
1293   si->soft_scrolling = TRUE;
1294   si->fading = FALSE;
1295   si->autorecord = TRUE;
1296   si->quick_doors = FALSE;
1297   si->team_mode = FALSE;
1298   si->handicap = TRUE;
1299   si->time_limit = TRUE;
1300   si->fullscreen = FALSE;
1301   si->ask_on_escape = TRUE;
1302
1303   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1304   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1305   si->music_set = getStringCopy(MUSIC_SUBDIR);
1306   si->override_level_graphics = FALSE;
1307   si->override_level_sounds = FALSE;
1308   si->override_level_music = FALSE;
1309
1310   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1311   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1312   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1313
1314   for (i=0; i<MAX_PLAYERS; i++)
1315   {
1316     si->input[i].use_joystick = FALSE;
1317     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1318     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1319     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1320     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1321     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1322     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1323     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1324     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1325     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1326     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1327     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1328     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1329     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1330     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1331     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1332   }
1333 }
1334
1335 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1336 {
1337   int i, pnr;
1338
1339   if (!setup_file_list)
1340     return;
1341
1342   /* global setup */
1343   si = setup;
1344   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1345     setSetupInfo(global_setup_tokens, i,
1346                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1347   setup = si;
1348
1349   /* shortcut setup */
1350   ssi = setup.shortcut;
1351   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1352     setSetupInfo(shortcut_setup_tokens, i,
1353                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1354   setup.shortcut = ssi;
1355
1356   /* player setup */
1357   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1358   {
1359     char prefix[30];
1360
1361     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1362
1363     sii = setup.input[pnr];
1364     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1365     {
1366       char full_token[100];
1367
1368       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1369       setSetupInfo(player_setup_tokens, i,
1370                    getTokenValue(setup_file_list, full_token));
1371     }
1372     setup.input[pnr] = sii;
1373   }
1374 }
1375
1376 void LoadSetup()
1377 {
1378   char *filename = getSetupFilename();
1379   struct SetupFileList *setup_file_list = NULL;
1380
1381   /* always start with reliable default values */
1382   setSetupInfoToDefaults(&setup);
1383
1384   setup_file_list = loadSetupFileList(filename);
1385
1386   if (setup_file_list)
1387   {
1388     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1389     decodeSetupFileList(setup_file_list);
1390
1391     setup.direct_draw = !setup.double_buffering;
1392
1393     freeSetupFileList(setup_file_list);
1394
1395     /* needed to work around problems with fixed length strings */
1396     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1397       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1398     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1399     {
1400       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1401
1402       strcpy(new_name, setup.player_name);
1403       free(setup.player_name);
1404       setup.player_name = new_name;
1405     }
1406   }
1407   else
1408     Error(ERR_WARN, "using default setup values");
1409 }
1410
1411 void SaveSetup()
1412 {
1413   char *filename = getSetupFilename();
1414   FILE *file;
1415   int i, pnr;
1416
1417   InitUserDataDirectory();
1418
1419   if (!(file = fopen(filename, MODE_WRITE)))
1420   {
1421     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1422     return;
1423   }
1424
1425   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1426                                                getCookie("SETUP")));
1427   fprintf(file, "\n");
1428
1429   /* global setup */
1430   si = setup;
1431   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1432   {
1433     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1434
1435     /* just to make things nicer :) */
1436     if (i == SETUP_TOKEN_PLAYER_NAME || i == SETUP_TOKEN_GRAPHICS_SET - 1)
1437       fprintf(file, "\n");
1438   }
1439
1440   /* shortcut setup */
1441   ssi = setup.shortcut;
1442   fprintf(file, "\n");
1443   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1444     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1445
1446   /* player setup */
1447   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1448   {
1449     char prefix[30];
1450
1451     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1452     fprintf(file, "\n");
1453
1454     sii = setup.input[pnr];
1455     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1456       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1457   }
1458
1459   fclose(file);
1460
1461   SetFilePermissions(filename, PERMS_PRIVATE);
1462 }