rnd-20030312-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_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 /* system setup */
1383 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
1384 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
1385
1386 #define NUM_SYSTEM_SETUP_TOKENS                 2
1387
1388 /* options setup */
1389 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
1390
1391 #define NUM_OPTIONS_SETUP_TOKENS                1
1392
1393
1394 static struct SetupInfo si;
1395 static struct SetupEditorInfo sei;
1396 static struct SetupShortcutInfo ssi;
1397 static struct SetupInputInfo sii;
1398 static struct SetupSystemInfo syi;
1399 static struct OptionInfo soi;
1400
1401 static struct TokenInfo global_setup_tokens[] =
1402 {
1403   { TYPE_STRING, &si.player_name,       "player_name"                   },
1404   { TYPE_SWITCH, &si.sound,             "sound"                         },
1405   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1406   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1407   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1408   { TYPE_SWITCH, &si.toons,             "toons"                         },
1409   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1410   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1411   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1412   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1413   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1414   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1415   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1416   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1417   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1418   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1419   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1420   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1421   { TYPE_STRING, &si.music_set,         "music_set"                     },
1422   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1423   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1424   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1425 };
1426
1427 static struct TokenInfo editor_setup_tokens[] =
1428 {
1429   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1430   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1431   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1432   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1433   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1434   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1435   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1436   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1437   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1438 };
1439
1440 static struct TokenInfo shortcut_setup_tokens[] =
1441 {
1442   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1443   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1444   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1445 };
1446
1447 static struct TokenInfo player_setup_tokens[] =
1448 {
1449   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1450   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1451   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1452   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1453   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1454   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1455   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1456   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1457   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1458   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1459   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1460   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1461   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1462   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1463   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1464   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1465 };
1466
1467 static struct TokenInfo system_setup_tokens[] =
1468 {
1469   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
1470   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1471 };
1472
1473 static struct TokenInfo options_setup_tokens[] =
1474 {
1475   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
1476 };
1477
1478 static void setSetupInfoToDefaults(struct SetupInfo *si)
1479 {
1480   int i;
1481
1482   si->player_name = getStringCopy(getLoginName());
1483
1484   si->sound = TRUE;
1485   si->sound_loops = TRUE;
1486   si->sound_music = TRUE;
1487   si->sound_simple = TRUE;
1488   si->toons = TRUE;
1489   si->double_buffering = TRUE;
1490   si->direct_draw = !si->double_buffering;
1491   si->scroll_delay = TRUE;
1492   si->soft_scrolling = TRUE;
1493   si->fading = FALSE;
1494   si->autorecord = TRUE;
1495   si->quick_doors = FALSE;
1496   si->team_mode = FALSE;
1497   si->handicap = TRUE;
1498   si->time_limit = TRUE;
1499   si->fullscreen = FALSE;
1500   si->ask_on_escape = TRUE;
1501
1502   si->graphics_set = getStringCopy(GRAPHICS_SUBDIR);
1503   si->sounds_set = getStringCopy(SOUNDS_SUBDIR);
1504   si->music_set = getStringCopy(MUSIC_SUBDIR);
1505   si->override_level_graphics = FALSE;
1506   si->override_level_sounds = FALSE;
1507   si->override_level_music = FALSE;
1508
1509   si->editor.el_boulderdash = TRUE;
1510   si->editor.el_emerald_mine = TRUE;
1511   si->editor.el_more = TRUE;
1512   si->editor.el_sokoban = TRUE;
1513   si->editor.el_supaplex = TRUE;
1514   si->editor.el_diamond_caves = TRUE;
1515   si->editor.el_dx_boulderdash = TRUE;
1516   si->editor.el_chars = TRUE;
1517   si->editor.el_custom = TRUE;
1518
1519   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
1520   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
1521   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
1522
1523   for (i=0; i<MAX_PLAYERS; i++)
1524   {
1525     si->input[i].use_joystick = FALSE;
1526     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
1527     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
1528     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
1529     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
1530     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
1531     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
1532     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
1533     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
1534     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
1535     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
1536     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
1537     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
1538     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
1539     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
1540     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
1541   }
1542
1543   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
1544   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
1545
1546   si->options.verbose = FALSE;
1547 }
1548
1549 static void decodeSetupFileList(struct SetupFileList *setup_file_list)
1550 {
1551   int i, pnr;
1552
1553   if (!setup_file_list)
1554     return;
1555
1556   /* global setup */
1557   si = setup;
1558   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1559     setSetupInfo(global_setup_tokens, i,
1560                  getTokenValue(setup_file_list, global_setup_tokens[i].text));
1561   setup = si;
1562
1563   /* editor setup */
1564   sei = setup.editor;
1565   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1566     setSetupInfo(editor_setup_tokens, i,
1567                  getTokenValue(setup_file_list,editor_setup_tokens[i].text));
1568   setup.editor = sei;
1569
1570   /* shortcut setup */
1571   ssi = setup.shortcut;
1572   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1573     setSetupInfo(shortcut_setup_tokens, i,
1574                  getTokenValue(setup_file_list,shortcut_setup_tokens[i].text));
1575   setup.shortcut = ssi;
1576
1577   /* player setup */
1578   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1579   {
1580     char prefix[30];
1581
1582     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1583
1584     sii = setup.input[pnr];
1585     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1586     {
1587       char full_token[100];
1588
1589       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
1590       setSetupInfo(player_setup_tokens, i,
1591                    getTokenValue(setup_file_list, full_token));
1592     }
1593     setup.input[pnr] = sii;
1594   }
1595
1596   /* system setup */
1597   syi = setup.system;
1598   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1599     setSetupInfo(system_setup_tokens, i,
1600                  getTokenValue(setup_file_list, system_setup_tokens[i].text));
1601   setup.system = syi;
1602
1603   /* options setup */
1604   soi = setup.options;
1605   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1606     setSetupInfo(options_setup_tokens, i,
1607                  getTokenValue(setup_file_list, options_setup_tokens[i].text));
1608   setup.options = soi;
1609 }
1610
1611 void LoadSetup()
1612 {
1613   char *filename = getSetupFilename();
1614   struct SetupFileList *setup_file_list = NULL;
1615
1616   /* always start with reliable default values */
1617   setSetupInfoToDefaults(&setup);
1618
1619   setup_file_list = loadSetupFileList(filename);
1620
1621   if (setup_file_list)
1622   {
1623     checkSetupFileListIdentifier(setup_file_list, getCookie("SETUP"));
1624     decodeSetupFileList(setup_file_list);
1625
1626     setup.direct_draw = !setup.double_buffering;
1627
1628     freeSetupFileList(setup_file_list);
1629
1630     /* needed to work around problems with fixed length strings */
1631     if (strlen(setup.player_name) > MAX_PLAYER_NAME_LEN)
1632       setup.player_name[MAX_PLAYER_NAME_LEN] = '\0';
1633     else if (strlen(setup.player_name) < MAX_PLAYER_NAME_LEN)
1634     {
1635       char *new_name = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
1636
1637       strcpy(new_name, setup.player_name);
1638       free(setup.player_name);
1639       setup.player_name = new_name;
1640     }
1641   }
1642   else
1643     Error(ERR_WARN, "using default setup values");
1644 }
1645
1646 void SaveSetup()
1647 {
1648   char *filename = getSetupFilename();
1649   FILE *file;
1650   int i, pnr;
1651
1652   InitUserDataDirectory();
1653
1654   if (!(file = fopen(filename, MODE_WRITE)))
1655   {
1656     Error(ERR_WARN, "cannot write setup file '%s'", filename);
1657     return;
1658   }
1659
1660   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
1661                                                getCookie("SETUP")));
1662   fprintf(file, "\n");
1663
1664   /* global setup */
1665   si = setup;
1666   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
1667   {
1668     /* just to make things nicer :) */
1669     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
1670         i == SETUP_TOKEN_GRAPHICS_SET)
1671       fprintf(file, "\n");
1672
1673     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
1674   }
1675
1676   /* editor setup */
1677   sei = setup.editor;
1678   fprintf(file, "\n");
1679   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
1680     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
1681
1682   /* shortcut setup */
1683   ssi = setup.shortcut;
1684   fprintf(file, "\n");
1685   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
1686     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
1687
1688   /* player setup */
1689   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
1690   {
1691     char prefix[30];
1692
1693     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
1694     fprintf(file, "\n");
1695
1696     sii = setup.input[pnr];
1697     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
1698       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
1699   }
1700
1701   /* system setup */
1702   syi = setup.system;
1703   fprintf(file, "\n");
1704   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
1705     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
1706
1707   /* options setup */
1708   soi = setup.options;
1709   fprintf(file, "\n");
1710   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
1711     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
1712
1713   fclose(file);
1714
1715   SetFilePermissions(filename, PERMS_PRIVATE);
1716 }
1717
1718 void LoadCustomElementDescriptions()
1719 {
1720   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1721   struct SetupFileList *setup_file_list;
1722   int i;
1723
1724   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1725   {
1726     if (element_info[i].custom_description != NULL)
1727     {
1728       free(element_info[i].custom_description);
1729       element_info[i].custom_description = NULL;
1730     }
1731   }
1732
1733   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1734     return;
1735
1736   for (i=0; i<NUM_FILE_ELEMENTS; i++)
1737   {
1738     char *token = getStringCat2(element_info[i].token_name, ".name");
1739     char *value = getTokenValue(setup_file_list, token);
1740
1741     if (value != NULL)
1742       element_info[i].custom_description = getStringCopy(value);
1743
1744     free(token);
1745   }
1746
1747   freeSetupFileList(setup_file_list);
1748 }
1749
1750 void LoadSpecialMenuDesignSettings()
1751 {
1752   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
1753   struct SetupFileList *setup_file_list;
1754   char *value;
1755
1756   /* !!! CHANGE THIS !!! (redundant initialization) !!! */
1757   global.num_toons = 20;
1758   global.menu_draw_xoffset = 0;
1759   global.menu_draw_yoffset = 0;
1760   global.menu_draw_xoffset_MAIN = 0;
1761   global.menu_draw_yoffset_MAIN = 0;
1762   global.door_step_offset = 2;
1763   global.door_step_delay = 10;
1764
1765   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
1766     return;
1767
1768   value = getTokenValue(setup_file_list, "global.num_toons");
1769   if (value != NULL)
1770     global.num_toons = get_integer_from_string(value);
1771
1772   value = getTokenValue(setup_file_list, "menu.draw_xoffset");
1773   if (value != NULL)
1774     global.menu_draw_xoffset = get_integer_from_string(value);
1775
1776   value = getTokenValue(setup_file_list, "menu.draw_yoffset");
1777   if (value != NULL)
1778     global.menu_draw_yoffset = get_integer_from_string(value);
1779
1780   value = getTokenValue(setup_file_list, "menu.draw_xoffset.MAIN");
1781   if (value != NULL)
1782     global.menu_draw_xoffset_MAIN = get_integer_from_string(value);
1783
1784   value = getTokenValue(setup_file_list, "menu.draw_yoffset.MAIN");
1785   if (value != NULL)
1786     global.menu_draw_yoffset_MAIN = get_integer_from_string(value);
1787
1788   value = getTokenValue(setup_file_list, "door.step_offset");
1789   if (value != NULL)
1790     global.door_step_offset = get_integer_from_string(value);
1791
1792   value = getTokenValue(setup_file_list, "door.step_delay");
1793   if (value != NULL)
1794     global.door_step_delay = get_integer_from_string(value);
1795
1796   freeSetupFileList(setup_file_list);
1797 }