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