rnd-20030304-2-src
[rocksndiamonds.git] / src / files.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2002 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * files.c                                                  *
12 ***********************************************************/
13
14 #include <ctype.h>
15 #include <sys/stat.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "tools.h"
21 #include "tape.h"
22
23
24 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
25 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
26 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
27 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
28 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
29 #define LEVEL_HEADER_UNUSED     14      /* unused level header bytes  */
30 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
31 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
32 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
33 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
34
35 /* file identifier strings */
36 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
37 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
38 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
39
40
41 /* ========================================================================= */
42 /* level file functions                                                      */
43 /* ========================================================================= */
44
45 static void setLevelInfoToDefaults()
46 {
47   int i, x, y;
48
49   level.file_version = FILE_VERSION_ACTUAL;
50   level.game_version = GAME_VERSION_ACTUAL;
51
52   level.encoding_16bit_field = FALSE;   /* default: only 8-bit elements */
53   level.encoding_16bit_yamyam = FALSE;  /* default: only 8-bit elements */
54   level.encoding_16bit_amoeba = FALSE;  /* default: only 8-bit elements */
55
56   lev_fieldx = level.fieldx = STD_LEV_FIELDX;
57   lev_fieldy = level.fieldy = STD_LEV_FIELDY;
58
59   for(x=0; x<MAX_LEV_FIELDX; x++)
60     for(y=0; y<MAX_LEV_FIELDY; y++)
61       Feld[x][y] = Ur[x][y] = EL_SAND;
62
63   level.time = 100;
64   level.gems_needed = 0;
65   level.amoeba_speed = 10;
66   level.time_magic_wall = 10;
67   level.time_wheel = 10;
68   level.time_light = 10;
69   level.time_timegate = 10;
70   level.amoeba_content = EL_DIAMOND;
71   level.double_speed = FALSE;
72   level.gravity = FALSE;
73   level.em_slippery_gems = FALSE;
74
75   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
76     level.name[i] = '\0';
77   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
78     level.author[i] = '\0';
79
80   strcpy(level.name, NAMELESS_LEVEL_NAME);
81   strcpy(level.author, ANONYMOUS_NAME);
82
83   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
84     level.score[i] = 10;
85
86   level.num_yam_contents = STD_ELEMENT_CONTENTS;
87   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
88     for(x=0; x<3; x++)
89       for(y=0; y<3; y++)
90         level.yam_content[i][x][y] =
91           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
92
93   Feld[0][0] = Ur[0][0] = EL_PLAYER1;
94   Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
95     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
96
97   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_PLAYER1;
147   else if (element == EL_KEY_OBSOLETE)
148     element = EL_KEY1;
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_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
876 {
877   int i, j;
878   int chunk_size_expected =
879     (tape->num_participating_players + 1) * tape->length;
880
881   if (chunk_size_expected != chunk_size)
882   {
883     ReadUnusedBytesFromFile(file, chunk_size);
884     return chunk_size_expected;
885   }
886
887   for(i=0; i<tape->length; i++)
888   {
889     if (i >= MAX_TAPELEN)
890       break;
891
892     for(j=0; j<MAX_PLAYERS; j++)
893     {
894       tape->pos[i].action[j] = MV_NO_MOVING;
895
896       if (tape->player_participates[j])
897         tape->pos[i].action[j] = fgetc(file);
898     }
899
900     tape->pos[i].delay = fgetc(file);
901
902     if (tape->file_version == FILE_VERSION_1_0)
903     {
904       /* eliminate possible diagonal moves in old tapes */
905       /* this is only for backward compatibility */
906
907       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
908       byte action = tape->pos[i].action[0];
909       int k, num_moves = 0;
910
911       for (k=0; k<4; k++)
912       {
913         if (action & joy_dir[k])
914         {
915           tape->pos[i + num_moves].action[0] = joy_dir[k];
916           if (num_moves > 0)
917             tape->pos[i + num_moves].delay = 0;
918           num_moves++;
919         }
920       }
921
922       if (num_moves > 1)
923       {
924         num_moves--;
925         i += num_moves;
926         tape->length += num_moves;
927       }
928     }
929     else if (tape->file_version < FILE_VERSION_2_0)
930     {
931       /* convert pre-2.0 tapes to new tape format */
932
933       if (tape->pos[i].delay > 1)
934       {
935         /* action part */
936         tape->pos[i + 1] = tape->pos[i];
937         tape->pos[i + 1].delay = 1;
938
939         /* delay part */
940         for(j=0; j<MAX_PLAYERS; j++)
941           tape->pos[i].action[j] = MV_NO_MOVING;
942         tape->pos[i].delay--;
943
944         i++;
945         tape->length++;
946       }
947     }
948
949     if (feof(file))
950       break;
951   }
952
953   if (i != tape->length)
954     chunk_size = (tape->num_participating_players + 1) * i;
955
956   return chunk_size;
957 }
958
959 void LoadTapeFromFilename(char *filename)
960 {
961   char cookie[MAX_LINE_LEN];
962   char chunk_name[CHUNK_ID_LEN + 1];
963   FILE *file;
964   int chunk_size;
965
966   /* always start with reliable default values */
967   setTapeInfoToDefaults();
968
969   if (!(file = fopen(filename, MODE_READ)))
970     return;
971
972   getFileChunkBE(file, chunk_name, NULL);
973   if (strcmp(chunk_name, "RND1") == 0)
974   {
975     getFile32BitBE(file);               /* not used */
976
977     getFileChunkBE(file, chunk_name, NULL);
978     if (strcmp(chunk_name, "TAPE") != 0)
979     {
980       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
981       fclose(file);
982       return;
983     }
984   }
985   else  /* check for pre-2.0 file format with cookie string */
986   {
987     strcpy(cookie, chunk_name);
988     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
989     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
990       cookie[strlen(cookie) - 1] = '\0';
991
992     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
993     {
994       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
995       fclose(file);
996       return;
997     }
998
999     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1000     {
1001       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1002       fclose(file);
1003       return;
1004     }
1005
1006     /* pre-2.0 tape files have no game version, so use file version here */
1007     tape.game_version = tape.file_version;
1008   }
1009
1010   if (tape.file_version < FILE_VERSION_1_2)
1011   {
1012     /* tape files from versions before 1.2.0 without chunk structure */
1013     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1014     LoadTape_BODY(file, 2 * tape.length,  &tape);
1015   }
1016   else
1017   {
1018     static struct
1019     {
1020       char *name;
1021       int size;
1022       int (*loader)(FILE *, int, struct TapeInfo *);
1023     }
1024     chunk_info[] =
1025     {
1026       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1027       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1028       { "BODY", -1,                     LoadTape_BODY },
1029       {  NULL,  0,                      NULL }
1030     };
1031
1032     while (getFileChunkBE(file, chunk_name, &chunk_size))
1033     {
1034       int i = 0;
1035
1036       while (chunk_info[i].name != NULL &&
1037              strcmp(chunk_name, chunk_info[i].name) != 0)
1038         i++;
1039
1040       if (chunk_info[i].name == NULL)
1041       {
1042         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1043               chunk_name, filename);
1044         ReadUnusedBytesFromFile(file, chunk_size);
1045       }
1046       else if (chunk_info[i].size != -1 &&
1047                chunk_info[i].size != chunk_size)
1048       {
1049         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1050               chunk_size, chunk_name, filename);
1051         ReadUnusedBytesFromFile(file, chunk_size);
1052       }
1053       else
1054       {
1055         /* call function to load this tape chunk */
1056         int chunk_size_expected =
1057           (chunk_info[i].loader)(file, chunk_size, &tape);
1058
1059         /* the size of some chunks cannot be checked before reading other
1060            chunks first (like "HEAD" and "BODY") that contain some header
1061            information, so check them here */
1062         if (chunk_size_expected != chunk_size)
1063         {
1064           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1065                 chunk_size, chunk_name, filename);
1066         }
1067       }
1068     }
1069   }
1070
1071   fclose(file);
1072
1073   tape.length_seconds = GetTapeLength();
1074 }
1075
1076 void LoadTape(int level_nr)
1077 {
1078   char *filename = getTapeFilename(level_nr);
1079
1080   LoadTapeFromFilename(filename);
1081 }
1082
1083 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1084 {
1085   putFileVersion(file, tape->file_version);
1086   putFileVersion(file, tape->game_version);
1087 }
1088
1089 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1090 {
1091   int i;
1092   byte store_participating_players = 0;
1093
1094   /* set bits for participating players for compact storage */
1095   for(i=0; i<MAX_PLAYERS; i++)
1096     if (tape->player_participates[i])
1097       store_participating_players |= (1 << i);
1098
1099   putFile32BitBE(file, tape->random_seed);
1100   putFile32BitBE(file, tape->date);
1101   putFile32BitBE(file, tape->length);
1102
1103   fputc(store_participating_players, file);
1104
1105   /* unused bytes not at the end here for 4-byte alignment of engine_version */
1106   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1107
1108   putFileVersion(file, tape->engine_version);
1109 }
1110
1111 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1112 {
1113   int i, j;
1114
1115   for(i=0; i<tape->length; i++)
1116   {
1117     for(j=0; j<MAX_PLAYERS; j++)
1118       if (tape->player_participates[j])
1119         fputc(tape->pos[i].action[j], file);
1120
1121     fputc(tape->pos[i].delay, file);
1122   }
1123 }
1124
1125 void SaveTape(int level_nr)
1126 {
1127   int i;
1128   char *filename = getTapeFilename(level_nr);
1129   FILE *file;
1130   boolean new_tape = TRUE;
1131   int num_participating_players = 0;
1132   int body_chunk_size;
1133
1134   InitTapeDirectory(leveldir_current->filename);
1135
1136   /* if a tape still exists, ask to overwrite it */
1137   if (access(filename, F_OK) == 0)
1138   {
1139     new_tape = FALSE;
1140     if (!Request("Replace old tape ?", REQ_ASK))
1141       return;
1142   }
1143
1144   if (!(file = fopen(filename, MODE_WRITE)))
1145   {
1146     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1147     return;
1148   }
1149
1150   tape.file_version = FILE_VERSION_ACTUAL;
1151   tape.game_version = GAME_VERSION_ACTUAL;
1152
1153   /* count number of participating players  */
1154   for(i=0; i<MAX_PLAYERS; i++)
1155     if (tape.player_participates[i])
1156       num_participating_players++;
1157
1158   body_chunk_size = (num_participating_players + 1) * tape.length;
1159
1160   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1161   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1162
1163   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1164   SaveTape_VERS(file, &tape);
1165
1166   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1167   SaveTape_HEAD(file, &tape);
1168
1169   putFileChunkBE(file, "BODY", body_chunk_size);
1170   SaveTape_BODY(file, &tape);
1171
1172   fclose(file);
1173
1174   SetFilePermissions(filename, PERMS_PRIVATE);
1175
1176   tape.changed = FALSE;
1177
1178   if (new_tape)
1179     Request("tape saved !", REQ_CONFIRM);
1180 }
1181
1182 void DumpTape(struct TapeInfo *tape)
1183 {
1184   int i, j;
1185
1186   if (TAPE_IS_EMPTY(*tape))
1187   {
1188     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1189     return;
1190   }
1191
1192   printf_line("-", 79);
1193   printf("Tape of Level %03d (file version %06d, game version %06d)\n",
1194          tape->level_nr, tape->file_version, tape->game_version);
1195   printf_line("-", 79);
1196
1197   for(i=0; i<tape->length; i++)
1198   {
1199     if (i >= MAX_TAPELEN)
1200       break;
1201
1202     printf("%03d: ", i);
1203
1204     for(j=0; j<MAX_PLAYERS; j++)
1205     {
1206       if (tape->player_participates[j])
1207       {
1208         int action = tape->pos[i].action[j];
1209
1210         printf("%d:%02x ", j, action);
1211         printf("[%c%c%c%c|%c%c] - ",
1212                (action & JOY_LEFT ? '<' : ' '),
1213                (action & JOY_RIGHT ? '>' : ' '),
1214                (action & JOY_UP ? '^' : ' '),
1215                (action & JOY_DOWN ? 'v' : ' '),
1216                (action & JOY_BUTTON_1 ? '1' : ' '),
1217                (action & JOY_BUTTON_2 ? '2' : ' '));
1218       }
1219     }
1220
1221     printf("(%03d)\n", tape->pos[i].delay);
1222   }
1223
1224   printf_line("-", 79);
1225 }
1226
1227
1228 /* ========================================================================= */
1229 /* score file functions                                                      */
1230 /* ========================================================================= */
1231
1232 void LoadScore(int level_nr)
1233 {
1234   int i;
1235   char *filename = getScoreFilename(level_nr);
1236   char cookie[MAX_LINE_LEN];
1237   char line[MAX_LINE_LEN];
1238   char *line_ptr;
1239   FILE *file;
1240
1241   /* always start with reliable default values */
1242   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1243   {
1244     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1245     highscore[i].Score = 0;
1246   }
1247
1248   if (!(file = fopen(filename, MODE_READ)))
1249     return;
1250
1251   /* check file identifier */
1252   fgets(cookie, MAX_LINE_LEN, file);
1253   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1254     cookie[strlen(cookie) - 1] = '\0';
1255
1256   if (!checkCookieString(cookie, SCORE_COOKIE))
1257   {
1258     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1259     fclose(file);
1260     return;
1261   }
1262
1263   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1264   {
1265     fscanf(file, "%d", &highscore[i].Score);
1266     fgets(line, MAX_LINE_LEN, file);
1267
1268     if (line[strlen(line) - 1] == '\n')
1269       line[strlen(line) - 1] = '\0';
1270
1271     for (line_ptr = line; *line_ptr; line_ptr++)
1272     {
1273       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1274       {
1275         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1276         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1277         break;
1278       }
1279     }
1280   }
1281
1282   fclose(file);
1283 }
1284
1285 void SaveScore(int level_nr)
1286 {
1287   int i;
1288   char *filename = getScoreFilename(level_nr);
1289   FILE *file;
1290
1291   InitScoreDirectory(leveldir_current->filename);
1292
1293   if (!(file = fopen(filename, MODE_WRITE)))
1294   {
1295     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1296     return;
1297   }
1298
1299   fprintf(file, "%s\n\n", SCORE_COOKIE);
1300
1301   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1302     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1303
1304   fclose(file);
1305
1306   SetFilePermissions(filename, PERMS_PUBLIC);
1307 }
1308
1309
1310 /* ========================================================================= */
1311 /* setup file functions                                                      */
1312 /* ========================================================================= */
1313
1314 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1315
1316 /* global setup */
1317 #define SETUP_TOKEN_PLAYER_NAME                 0
1318 #define SETUP_TOKEN_SOUND                       1
1319 #define SETUP_TOKEN_SOUND_LOOPS                 2
1320 #define SETUP_TOKEN_SOUND_MUSIC                 3
1321 #define SETUP_TOKEN_SOUND_SIMPLE                4
1322 #define SETUP_TOKEN_TOONS                       5
1323 #define SETUP_TOKEN_SCROLL_DELAY                6
1324 #define SETUP_TOKEN_SOFT_SCROLLING              7
1325 #define SETUP_TOKEN_FADING                      8
1326 #define SETUP_TOKEN_AUTORECORD                  9
1327 #define SETUP_TOKEN_QUICK_DOORS                 10
1328 #define SETUP_TOKEN_TEAM_MODE                   11
1329 #define SETUP_TOKEN_HANDICAP                    12
1330 #define SETUP_TOKEN_TIME_LIMIT                  13
1331 #define SETUP_TOKEN_FULLSCREEN                  14
1332 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1333 #define SETUP_TOKEN_GRAPHICS_SET                16
1334 #define SETUP_TOKEN_SOUNDS_SET                  17
1335 #define SETUP_TOKEN_MUSIC_SET                   18
1336 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1337 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1338 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1339
1340 #define NUM_GLOBAL_SETUP_TOKENS                 22
1341
1342 /* editor setup */
1343 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
1344 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
1345 #define SETUP_TOKEN_EDITOR_EL_MORE              2
1346 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
1347 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
1348 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
1349 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
1350 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
1351 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
1352
1353 #define NUM_EDITOR_SETUP_TOKENS                 9
1354
1355 /* shortcut setup */
1356 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
1357 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
1358 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
1359
1360 #define NUM_SHORTCUT_SETUP_TOKENS               3
1361
1362 /* player setup */
1363 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
1364 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
1365 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
1366 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
1367 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
1368 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
1369 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
1370 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
1371 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
1372 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
1373 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
1374 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
1375 #define SETUP_TOKEN_PLAYER_KEY_UP               12
1376 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
1377 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
1378 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
1379
1380 #define NUM_PLAYER_SETUP_TOKENS                 16
1381
1382 static struct SetupInfo si;
1383 static struct SetupEditorInfo sei;
1384 static struct SetupShortcutInfo ssi;
1385 static struct SetupInputInfo sii;
1386
1387 static struct TokenInfo global_setup_tokens[] =
1388 {
1389   /* global setup */
1390   { TYPE_STRING, &si.player_name,       "player_name"                   },
1391   { TYPE_SWITCH, &si.sound,             "sound"                         },
1392   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1393   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1394   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1395   { TYPE_SWITCH, &si.toons,             "toons"                         },
1396   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1397   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1398   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1399   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1400   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1401   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1402   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1403   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1404   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1405   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1406   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1407   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1408   { TYPE_STRING, &si.music_set,         "music_set"                     },
1409   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1410   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1411   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1412 };
1413
1414 static struct TokenInfo editor_setup_tokens[] =
1415 {
1416   /* shortcut setup */
1417   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1418   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1419   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1420   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1421   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1422   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1423   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1424   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1425   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1426 };
1427
1428 static struct TokenInfo shortcut_setup_tokens[] =
1429 {
1430   /* shortcut setup */
1431   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1432   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1433   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1434 };
1435
1436 static struct TokenInfo player_setup_tokens[] =
1437 {
1438   /* player setup */
1439   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1440   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1441   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1442   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1443   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1444   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1445   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1446   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1447   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1448   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1449   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1450   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1451   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1452   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1453   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1454   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1455 };
1456
1457 static void setSetupInfoToDefaults(struct SetupInfo *si)
1458 {
1459   int i;
1460
1461   si->player_name = getStringCopy(getLoginName());
1462
1463   si->sound = TRUE;
1464   si->sound_loops = TRUE;
1465   si->sound_music = TRUE;
1466   si->sound_simple = TRUE;
1467   si->toons = TRUE;
1468   si->double_buffering = TRUE;
1469   si->direct_draw = !si->double_buffering;
1470   si->scroll_delay = TRUE;
1471   si->soft_scrolling = TRUE;
1472   si->fading = FALSE;
1473   si->autorecord = TRUE;
1474   si->quick_doors = FALSE;
1475   si->team_mode = FALSE;
1476   si->handicap = TRUE;
1477   si->time_limit = TRUE;
1478   si->fullscreen = FALSE;
1479   si->ask_on_escape = TRUE;
1480
1481   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1482   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1483   si->music_set = getStringCopy(MUSIC_SUBDIR);
1484   si->override_level_graphics = FALSE;
1485   si->override_level_sounds = FALSE;
1486   si->override_level_music = FALSE;
1487
1488   si->editor.el_boulderdash = TRUE;
1489   si->editor.el_emerald_mine = TRUE;
1490   si->editor.el_more = TRUE;
1491   si->editor.el_sokoban = TRUE;
1492   si->editor.el_supaplex = TRUE;
1493   si->editor.el_diamond_caves = TRUE;
1494   si->editor.el_dx_boulderdash = TRUE;
1495   si->editor.el_chars = TRUE;
1496   si->editor.el_custom = TRUE;
1497
1498   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1499   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1500   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1501
1502   for (i=0; i<MAX_PLAYERS; i++)
1503   {
1504     si->input[i].use_joystick = FALSE;
1505     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1506     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1507     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1508     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1509     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1510     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1511     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1512     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1513     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1514     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1515     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1516     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1517     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1518     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1519     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1520   }
1521 }
1522
1523 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1524 {
1525   int i, pnr;
1526
1527   if (!setup_file_list)
1528     return;
1529
1530   /* global setup */
1531   si = setup;
1532   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1533     setSetupInfo(global_setup_tokens, i,
1534                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1535   setup = si;
1536
1537   /* editor setup */
1538   sei = setup.editor;
1539   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1540     setSetupInfo(editor_setup_tokens, i,
1541                  getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1542   setup.editor = sei;
1543
1544   /* shortcut setup */
1545   ssi = setup.shortcut;
1546   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1547     setSetupInfo(shortcut_setup_tokens, i,
1548                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1549   setup.shortcut = ssi;
1550
1551   /* player setup */
1552   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1553   {
1554     char prefix[30];
1555
1556     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1557
1558     sii = setup.input[pnr];
1559     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1560     {
1561       char full_token[100];
1562
1563       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1564       setSetupInfo(player_setup_tokens, i,
1565                    getTokenValue(setup_file_list, full_token));
1566     }
1567     setup.input[pnr] = sii;
1568   }
1569 }
1570
1571 void LoadSetup()
1572 {
1573   char *filename = getSetupFilename();
1574   struct SetupFileList *setup_file_list = NULL;
1575
1576   /* always start with reliable default values */
1577   setSetupInfoToDefaults(&setup);
1578
1579   setup_file_list = loadSetupFileList(filename);
1580
1581   if (setup_file_list)
1582   {
1583     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1584     decodeSetupFileList(setup_file_list);
1585
1586     setup.direct_draw = !setup.double_buffering;
1587
1588     freeSetupFileList(setup_file_list);
1589
1590     /* needed to work around problems with fixed length strings */
1591     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1592       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1593     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1594     {
1595       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1596
1597       strcpy(new_name, setup.player_name);
1598       free(setup.player_name);
1599       setup.player_name = new_name;
1600     }
1601   }
1602   else
1603     Error(ERR_WARN, "using default setup values");
1604 }
1605
1606 void SaveSetup()
1607 {
1608   char *filename = getSetupFilename();
1609   FILE *file;
1610   int i, pnr;
1611
1612   InitUserDataDirectory();
1613
1614   if (!(file = fopen(filename, MODE_WRITE)))
1615   {
1616     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1617     return;
1618   }
1619
1620   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1621                                                getCookie("SETUP")));
1622   fprintf(file, "\n");
1623
1624   /* global setup */
1625   si = setup;
1626   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1627   {
1628     /* just to make things nicer :) */
1629     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1630         i == SETUP_TOKEN_GRAPHICS_SET)
1631       fprintf(file, "\n");
1632
1633     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1634   }
1635
1636   /* editor setup */
1637   sei = setup.editor;
1638   fprintf(file, "\n");
1639   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1640     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1641
1642   /* shortcut setup */
1643   ssi = setup.shortcut;
1644   fprintf(file, "\n");
1645   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1646     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1647
1648   /* player setup */
1649   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1650   {
1651     char prefix[30];
1652
1653     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1654     fprintf(file, "\n");
1655
1656     sii = setup.input[pnr];
1657     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1658       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1659   }
1660
1661   fclose(file);
1662
1663   SetFilePermissions(filename, PERMS_PRIVATE);
1664 }
1665
1666 void LoadCustomElementDescriptions()
1667 {
1668   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1669   struct SetupFileList *setup_file_list;
1670   int i;
1671
1672   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1673   {
1674     if (element_info[i].custom_description != NULL)
1675     {
1676       free(element_info[i].custom_description);
1677       element_info[i].custom_description = NULL;
1678     }
1679   }
1680
1681   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1682     return;
1683
1684   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1685   {
1686     char *token = getStringCat2(element_info[i].token_name, ".name");
1687     char *value = getTokenValue(setup_file_list, token);
1688
1689     if (value != NULL)
1690       element_info[i].custom_description = getStringCopy(value);
1691
1692     free(token);
1693   }
1694
1695   freeSetupFileList(setup_file_list);
1696 }
1697
1698 void LoadSpecialMenuDesignSettings()
1699 {
1700   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1701   struct SetupFileList *setup_file_list;
1702   char *value;
1703
1704   /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1705   global.num_toons = 20;
1706
1707   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1708     return;
1709
1710   value = getTokenValue(setup_file_list, "global.num_toons");
1711   if (value != NULL)
1712     global.num_toons = get_integer_from_string(value);
1713
1714   freeSetupFileList(setup_file_list);
1715 }