rnd-20030331-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 char *get_corrected_login_name(char *login_name)
1519 {
1520   /* needed because player name must be a fixed length string */
1521   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1522
1523   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
1524   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
1525
1526   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
1527     if (strchr(login_name_new, ' '))
1528       *strchr(login_name_new, ' ') = '\0';
1529
1530   return login_name_new;
1531 }
1532
1533 static void setSetupInfoToDefaults(struct SetupInfo *si)
1534 {
1535   int i;
1536
1537   si->player_name = get_corrected_login_name(getLoginName());
1538
1539   si->sound = TRUE;
1540   si->sound_loops = TRUE;
1541   si->sound_music = TRUE;
1542   si->sound_simple = TRUE;
1543   si->toons = TRUE;
1544   si->double_buffering = TRUE;
1545   si->direct_draw = !si->double_buffering;
1546   si->scroll_delay = TRUE;
1547   si->soft_scrolling = TRUE;
1548   si->fading = FALSE;
1549   si->autorecord = TRUE;
1550   si->quick_doors = FALSE;
1551   si->team_mode = FALSE;
1552   si->handicap = TRUE;
1553   si->time_limit = TRUE;
1554   si->fullscreen = FALSE;
1555   si->ask_on_escape = TRUE;
1556
1557   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1558   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1559   si->music_set = getStringCopy(MUSIC_SUBDIR);
1560   si->override_level_graphics = FALSE;
1561   si->override_level_sounds = FALSE;
1562   si->override_level_music = FALSE;
1563
1564   si->editor.el_boulderdash = TRUE;
1565   si->editor.el_emerald_mine = TRUE;
1566   si->editor.el_more = TRUE;
1567   si->editor.el_sokoban = TRUE;
1568   si->editor.el_supaplex = TRUE;
1569   si->editor.el_diamond_caves = TRUE;
1570   si->editor.el_dx_boulderdash = TRUE;
1571   si->editor.el_chars = TRUE;
1572   si->editor.el_custom = TRUE;
1573
1574   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1575   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1576   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1577
1578   for (i=0; i<MAX_PLAYERS; i++)
1579   {
1580     si->input[i].use_joystick = FALSE;
1581     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1582     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1583     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1584     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1585     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1586     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1587     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1588     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1589     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1590     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1591     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1592     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1593     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1594     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1595     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1596   }
1597
1598   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1599   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1600
1601   si->options.verbose = FALSE;
1602 }
1603
1604 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1605 {
1606   int i, pnr;
1607
1608   if (!setup_file_list)
1609     return;
1610
1611   /* global setup */
1612   si = setup;
1613   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1614     setSetupInfo(global_setup_tokens, i,
1615                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1616   setup = si;
1617
1618   /* editor setup */
1619   sei = setup.editor;
1620   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1621     setSetupInfo(editor_setup_tokens, i,
1622                  getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1623   setup.editor = sei;
1624
1625   /* shortcut setup */
1626   ssi = setup.shortcut;
1627   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1628     setSetupInfo(shortcut_setup_tokens, i,
1629                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1630   setup.shortcut = ssi;
1631
1632   /* player setup */
1633   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1634   {
1635     char prefix[30];
1636
1637     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1638
1639     sii = setup.input[pnr];
1640     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1641     {
1642       char full_token[100];
1643
1644       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1645       setSetupInfo(player_setup_tokens, i,
1646                    getTokenValue(setup_file_list, full_token));
1647     }
1648     setup.input[pnr] = sii;
1649   }
1650
1651   /* system setup */
1652   syi = setup.system;
1653   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1654     setSetupInfo(system_setup_tokens, i,
1655                  getTokenValue(setup_file_list, system_setup_tokens[i].text));
1656   setup.system = syi;
1657
1658   /* options setup */
1659   soi = setup.options;
1660   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1661     setSetupInfo(options_setup_tokens, i,
1662                  getTokenValue(setup_file_list, options_setup_tokens[i].text));
1663   setup.options = soi;
1664 }
1665
1666 void LoadSetup()
1667 {
1668   char *filename = getSetupFilename();
1669   struct SetupFileList *setup_file_list = NULL;
1670
1671   /* always start with reliable default values */
1672   setSetupInfoToDefaults(&setup);
1673
1674   setup_file_list = loadSetupFileList(filename);
1675
1676   if (setup_file_list)
1677   {
1678     char *player_name_new;
1679
1680     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1681     decodeSetupFileList(setup_file_list);
1682
1683     setup.direct_draw = !setup.double_buffering;
1684
1685     freeSetupFileList(setup_file_list);
1686
1687     /* needed to work around problems with fixed length strings */
1688     player_name_new = get_corrected_login_name(setup.player_name);
1689     free(setup.player_name);
1690     setup.player_name = player_name_new;
1691   }
1692   else
1693     Error(ERR_WARN, "using default setup values");
1694 }
1695
1696 void SaveSetup()
1697 {
1698   char *filename = getSetupFilename();
1699   FILE *file;
1700   int i, pnr;
1701
1702   InitUserDataDirectory();
1703
1704   if (!(file = fopen(filename, MODE_WRITE)))
1705   {
1706     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1707     return;
1708   }
1709
1710   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1711                                                getCookie("SETUP")));
1712   fprintf(file, "\n");
1713
1714   /* global setup */
1715   si = setup;
1716   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1717   {
1718     /* just to make things nicer :) */
1719     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1720         i == SETUP_TOKEN_GRAPHICS_SET)
1721       fprintf(file, "\n");
1722
1723     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1724   }
1725
1726   /* editor setup */
1727   sei = setup.editor;
1728   fprintf(file, "\n");
1729   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1730     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1731
1732   /* shortcut setup */
1733   ssi = setup.shortcut;
1734   fprintf(file, "\n");
1735   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1736     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1737
1738   /* player setup */
1739   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1740   {
1741     char prefix[30];
1742
1743     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1744     fprintf(file, "\n");
1745
1746     sii = setup.input[pnr];
1747     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1748       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1749   }
1750
1751   /* system setup */
1752   syi = setup.system;
1753   fprintf(file, "\n");
1754   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1755     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1756
1757   /* options setup */
1758   soi = setup.options;
1759   fprintf(file, "\n");
1760   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1761     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1762
1763   fclose(file);
1764
1765   SetFilePermissions(filename, PERMS_PRIVATE);
1766 }
1767
1768 void LoadCustomElementDescriptions()
1769 {
1770   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1771   struct SetupFileList *setup_file_list;
1772   int i;
1773
1774   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1775   {
1776     if (element_info[i].custom_description != NULL)
1777     {
1778       free(element_info[i].custom_description);
1779       element_info[i].custom_description = NULL;
1780     }
1781   }
1782
1783   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1784     return;
1785
1786   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1787   {
1788     char *token = getStringCat2(element_info[i].token_name, ".name");
1789     char *value = getTokenValue(setup_file_list, token);
1790
1791     if (value != NULL)
1792       element_info[i].custom_description = getStringCopy(value);
1793
1794     free(token);
1795   }
1796
1797   freeSetupFileList(setup_file_list);
1798 }
1799
1800 void LoadSpecialMenuDesignSettings()
1801 {
1802   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1803   struct SetupFileList *setup_file_list;
1804   char *value;
1805
1806   /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1807   global.num_toons = 20;
1808   global.menu_draw_xoffset = 0;
1809   global.menu_draw_yoffset = 0;
1810   global.menu_draw_xoffset_MAIN = 0;
1811   global.menu_draw_yoffset_MAIN = 0;
1812   global.door_step_offset = 2;
1813   global.door_step_delay = 10;
1814
1815   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1816     return;
1817
1818   value = getTokenValue(setup_file_list, "global.num_toons");
1819   if (value != NULL)
1820     global.num_toons = get_integer_from_string(value);
1821
1822   value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1823   if (value != NULL)
1824     global.menu_draw_xoffset = get_integer_from_string(value);
1825
1826   value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1827   if (value != NULL)
1828     global.menu_draw_yoffset = get_integer_from_string(value);
1829
1830   value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1831   if (value != NULL)
1832     global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1833
1834   value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1835   if (value != NULL)
1836     global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1837
1838   value = getTokenValue(setup_file_list, "door.step_offset");
1839   if (value != NULL)
1840     global.door_step_offset = get_integer_from_string(value);
1841
1842   value = getTokenValue(setup_file_list, "door.step_delay");
1843   if (value != NULL)
1844     global.door_step_delay = get_integer_from_string(value);
1845
1846   freeSetupFileList(setup_file_list);
1847 }