rnd-20030828-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 "init.h"
21 #include "tools.h"
22 #include "tape.h"
23
24
25 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
26 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
27 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
28 #define FILE_VERS_CHUNK_SIZE    8       /* size of file version chunk */
29 #define LEVEL_HEADER_SIZE       80      /* size of level file header  */
30 #define LEVEL_HEADER_UNUSED     13      /* unused level header bytes  */
31 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
32 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
33 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
34 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
35 #define TAPE_HEADER_SIZE        20      /* size of tape file header   */
36 #define TAPE_HEADER_UNUSED      3       /* unused tape header bytes   */
37
38 #define LEVEL_CHUNK_CUS3_SIZE(x) (2 + x * LEVEL_CPART_CUS3_SIZE)
39
40 /* file identifier strings */
41 #define LEVEL_COOKIE_TMPL       "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
42 #define TAPE_COOKIE_TMPL        "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
43 #define SCORE_COOKIE            "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
44
45
46 /* ========================================================================= */
47 /* level file functions                                                      */
48 /* ========================================================================= */
49
50 void setElementChangePages(struct ElementInfo *ei, int change_pages)
51 {
52   int change_page_size = sizeof(struct ElementChangeInfo);
53
54   ei->num_change_pages = MAX(1, change_pages);
55
56   ei->change_page =
57     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
58
59   if (ei->current_change_page >= ei->num_change_pages)
60     ei->current_change_page = ei->num_change_pages - 1;
61
62   ei->change = &ei->change_page[ei->current_change_page];
63 }
64
65 void setElementChangeInfoToDefaults(struct ElementChangeInfo *eci)
66 {
67   int x, y;
68
69   eci->events = CE_BITMASK_DEFAULT;
70   eci->target_element = EL_EMPTY_SPACE;
71
72   eci->delay_fixed = 0;
73   eci->delay_random = 0;
74   eci->delay_frames = -1;       /* later set to reliable default value */
75
76   eci->trigger_element = EL_EMPTY_SPACE;
77
78   eci->explode = FALSE;
79   eci->use_content = FALSE;
80   eci->only_complete = FALSE;
81   eci->use_random_change = FALSE;
82   eci->random = 0;
83   eci->power = CP_NON_DESTRUCTIVE;
84
85   for(x=0; x<3; x++)
86     for(y=0; y<3; y++)
87       eci->content[x][y] = EL_EMPTY_SPACE;
88
89   eci->player_action = 0;
90   eci->collide_action = 0;
91   eci->other_action = 0;
92
93   eci->pre_change_function = NULL;
94   eci->change_function = NULL;
95   eci->post_change_function = NULL;
96 }
97
98 static void setLevelInfoToDefaults(struct LevelInfo *level)
99 {
100   int i, j, x, y;
101
102   level->file_version = FILE_VERSION_ACTUAL;
103   level->game_version = GAME_VERSION_ACTUAL;
104
105   level->encoding_16bit_field  = FALSE; /* default: only 8-bit elements */
106   level->encoding_16bit_yamyam = FALSE; /* default: only 8-bit elements */
107   level->encoding_16bit_amoeba = FALSE; /* default: only 8-bit elements */
108
109   level->fieldx = STD_LEV_FIELDX;
110   level->fieldy = STD_LEV_FIELDY;
111
112   for(x=0; x<MAX_LEV_FIELDX; x++)
113     for(y=0; y<MAX_LEV_FIELDY; y++)
114       level->field[x][y] = EL_SAND;
115
116   level->time = 100;
117   level->gems_needed = 0;
118   level->amoeba_speed = 10;
119   level->time_magic_wall = 10;
120   level->time_wheel = 10;
121   level->time_light = 10;
122   level->time_timegate = 10;
123   level->amoeba_content = EL_DIAMOND;
124   level->double_speed = FALSE;
125   level->gravity = FALSE;
126   level->em_slippery_gems = FALSE;
127
128   level->use_custom_template = FALSE;
129
130   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
131     level->name[i] = '\0';
132   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
133     level->author[i] = '\0';
134
135   strcpy(level->name, NAMELESS_LEVEL_NAME);
136   strcpy(level->author, ANONYMOUS_NAME);
137
138   level->envelope[0] = '\0';
139   level->envelope_xsize = MAX_ENVELOPE_XSIZE;
140   level->envelope_ysize = MAX_ENVELOPE_YSIZE;
141
142   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
143     level->score[i] = 10;
144
145   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
146   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
147     for(x=0; x<3; x++)
148       for(y=0; y<3; y++)
149         level->yamyam_content[i][x][y] =
150           (i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
151
152   level->field[0][0] = EL_PLAYER_1;
153   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
154
155   for (i=0; i < MAX_NUM_ELEMENTS; i++)
156   {
157     setElementChangePages(&element_info[i], 1);
158     setElementChangeInfoToDefaults(element_info[i].change);
159   }
160
161   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
162   {
163     int element = EL_CUSTOM_START + i;
164
165     for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
166       element_info[element].description[j] = '\0';
167     if (element_info[element].custom_description != NULL)
168       strncpy(element_info[element].description,
169               element_info[element].custom_description, MAX_ELEMENT_NAME_LEN);
170     else
171       strcpy(element_info[element].description,
172              element_info[element].editor_description);
173
174     element_info[element].use_gfx_element = FALSE;
175     element_info[element].gfx_element = EL_EMPTY_SPACE;
176
177     element_info[element].collect_score = 10;           /* special default */
178     element_info[element].collect_count = 1;            /* special default */
179
180     element_info[element].push_delay_fixed = 2;         /* special default */
181     element_info[element].push_delay_random = 8;        /* special default */
182     element_info[element].move_delay_fixed = 0;
183     element_info[element].move_delay_random = 0;
184
185     element_info[element].move_pattern = MV_ALL_DIRECTIONS;
186     element_info[element].move_direction_initial = MV_NO_MOVING;
187     element_info[element].move_stepsize = TILEX / 8;
188
189     element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
190
191     for(x=0; x<3; x++)
192       for(y=0; y<3; y++)
193         element_info[element].content[x][y] = EL_EMPTY_SPACE;
194
195     element_info[element].access_type = 0;
196     element_info[element].access_layer = 0;
197     element_info[element].walk_to_action = 0;
198     element_info[element].smash_targets = 0;
199     element_info[element].deadliness = 0;
200     element_info[element].consistency = 0;
201
202     element_info[element].can_explode_by_fire = FALSE;
203     element_info[element].can_explode_smashed = FALSE;
204     element_info[element].can_explode_impact = FALSE;
205
206     element_info[element].current_change_page = 0;
207
208     /* start with no properties at all */
209     for (j=0; j < NUM_EP_BITFIELDS; j++)
210       Properties[element][j] = EP_BITMASK_DEFAULT;
211
212     element_info[element].modified_settings = FALSE;
213   }
214
215   BorderElement = EL_STEELWALL;
216
217   level->no_level_file = FALSE;
218
219   if (leveldir_current == NULL)         /* only when dumping level */
220     return;
221
222   /* try to determine better author name than 'anonymous' */
223   if (strcmp(leveldir_current->author, ANONYMOUS_NAME) != 0)
224   {
225     strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
226     level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
227   }
228   else
229   {
230     switch (LEVELCLASS(leveldir_current))
231     {
232       case LEVELCLASS_TUTORIAL:
233         strcpy(level->author, PROGRAM_AUTHOR_STRING);
234         break;
235
236       case LEVELCLASS_CONTRIBUTION:
237         strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
238         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
239         break;
240
241       case LEVELCLASS_USER:
242         strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
243         level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
244         break;
245
246       default:
247         /* keep default value */
248         break;
249     }
250   }
251 }
252
253 static void ActivateLevelTemplate()
254 {
255   /* Currently there is no special action needed to activate the template
256      data, because 'element_info' and 'Properties' overwrite the original
257      level data, while all other variables do not change. */
258 }
259
260 boolean LevelFileExists(int level_nr)
261 {
262   char *filename = getLevelFilename(level_nr);
263
264   return (access(filename, F_OK) == 0);
265 }
266
267 static int checkLevelElement(int element)
268 {
269   if (element >= NUM_FILE_ELEMENTS)
270   {
271     Error(ERR_WARN, "invalid level element %d", element);
272     element = EL_CHAR_QUESTION;
273   }
274   else if (element == EL_PLAYER_OBSOLETE)
275     element = EL_PLAYER_1;
276   else if (element == EL_KEY_OBSOLETE)
277     element = EL_KEY_1;
278
279   return element;
280 }
281
282 static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
283 {
284   level->file_version = getFileVersion(file);
285   level->game_version = getFileVersion(file);
286
287   return chunk_size;
288 }
289
290 static int LoadLevel_HEAD(FILE *file, int chunk_size, struct LevelInfo *level)
291 {
292   int i, x, y;
293
294   level->fieldx = getFile8Bit(file);
295   level->fieldy = getFile8Bit(file);
296
297   level->time           = getFile16BitBE(file);
298   level->gems_needed    = getFile16BitBE(file);
299
300   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
301     level->name[i] = getFile8Bit(file);
302   level->name[MAX_LEVEL_NAME_LEN] = 0;
303
304   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
305     level->score[i] = getFile8Bit(file);
306
307   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
308   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
309     for(y=0; y<3; y++)
310       for(x=0; x<3; x++)
311         level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
312
313   level->amoeba_speed           = getFile8Bit(file);
314   level->time_magic_wall        = getFile8Bit(file);
315   level->time_wheel             = getFile8Bit(file);
316   level->amoeba_content         = checkLevelElement(getFile8Bit(file));
317   level->double_speed           = (getFile8Bit(file) == 1 ? TRUE : FALSE);
318   level->gravity                = (getFile8Bit(file) == 1 ? TRUE : FALSE);
319   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
320   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
321
322   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
323
324   ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
325
326   return chunk_size;
327 }
328
329 static int LoadLevel_AUTH(FILE *file, int chunk_size, struct LevelInfo *level)
330 {
331   int i;
332
333   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
334     level->author[i] = getFile8Bit(file);
335   level->author[MAX_LEVEL_NAME_LEN] = 0;
336
337   return chunk_size;
338 }
339
340 static int LoadLevel_BODY(FILE *file, int chunk_size, struct LevelInfo *level)
341 {
342   int x, y;
343   int chunk_size_expected = level->fieldx * level->fieldy;
344
345   /* Note: "chunk_size" was wrong before version 2.0 when elements are
346      stored with 16-bit encoding (and should be twice as big then).
347      Even worse, playfield data was stored 16-bit when only yamyam content
348      contained 16-bit elements and vice versa. */
349
350   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
351     chunk_size_expected *= 2;
352
353   if (chunk_size_expected != chunk_size)
354   {
355     ReadUnusedBytesFromFile(file, chunk_size);
356     return chunk_size_expected;
357   }
358
359   for(y=0; y<level->fieldy; y++)
360     for(x=0; x<level->fieldx; x++)
361       level->field[x][y] =
362         checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
363                           getFile8Bit(file));
364   return chunk_size;
365 }
366
367 static int LoadLevel_CONT(FILE *file, int chunk_size, struct LevelInfo *level)
368 {
369   int i, x, y;
370   int header_size = 4;
371   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
372   int chunk_size_expected = header_size + content_size;
373
374   /* Note: "chunk_size" was wrong before version 2.0 when elements are
375      stored with 16-bit encoding (and should be twice as big then).
376      Even worse, playfield data was stored 16-bit when only yamyam content
377      contained 16-bit elements and vice versa. */
378
379   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
380     chunk_size_expected += content_size;
381
382   if (chunk_size_expected != chunk_size)
383   {
384     ReadUnusedBytesFromFile(file, chunk_size);
385     return chunk_size_expected;
386   }
387
388   getFile8Bit(file);
389   level->num_yamyam_contents = getFile8Bit(file);
390   getFile8Bit(file);
391   getFile8Bit(file);
392
393   /* correct invalid number of content fields -- should never happen */
394   if (level->num_yamyam_contents < 1 ||
395       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
396     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
397
398   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
399     for(y=0; y<3; y++)
400       for(x=0; x<3; x++)
401         level->yamyam_content[i][x][y] =
402           checkLevelElement(level->encoding_16bit_field ?
403                             getFile16BitBE(file) : getFile8Bit(file));
404   return chunk_size;
405 }
406
407 static int LoadLevel_CNT2(FILE *file, int chunk_size, struct LevelInfo *level)
408 {
409   int i, x, y;
410   int element;
411   int num_contents, content_xsize, content_ysize;
412   int content_array[MAX_ELEMENT_CONTENTS][3][3];
413
414   element = checkLevelElement(getFile16BitBE(file));
415   num_contents = getFile8Bit(file);
416   content_xsize = getFile8Bit(file);
417   content_ysize = getFile8Bit(file);
418   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
419
420   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
421     for(y=0; y<3; y++)
422       for(x=0; x<3; x++)
423         content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
424
425   /* correct invalid number of content fields -- should never happen */
426   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
427     num_contents = STD_ELEMENT_CONTENTS;
428
429   if (element == EL_YAMYAM)
430   {
431     level->num_yamyam_contents = num_contents;
432
433     for(i=0; i<num_contents; i++)
434       for(y=0; y<3; y++)
435         for(x=0; x<3; x++)
436           level->yamyam_content[i][x][y] = content_array[i][x][y];
437   }
438   else if (element == EL_BD_AMOEBA)
439   {
440     level->amoeba_content = content_array[0][0][0];
441   }
442   else
443   {
444     Error(ERR_WARN, "cannot load content for element '%d'", element);
445   }
446
447   return chunk_size;
448 }
449
450 static int LoadLevel_CUS1(FILE *file, int chunk_size, struct LevelInfo *level)
451 {
452   int num_changed_custom_elements = getFile16BitBE(file);
453   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
454   int i;
455
456   if (chunk_size_expected != chunk_size)
457   {
458     ReadUnusedBytesFromFile(file, chunk_size - 2);
459     return chunk_size_expected;
460   }
461
462   for (i=0; i < num_changed_custom_elements; i++)
463   {
464     int element = getFile16BitBE(file);
465     int properties = getFile32BitBE(file);
466
467     if (IS_CUSTOM_ELEMENT(element))
468       Properties[element][EP_BITFIELD_BASE] = properties;
469     else
470       Error(ERR_WARN, "invalid custom element number %d", element);
471   }
472
473   return chunk_size;
474 }
475
476 static int LoadLevel_CUS2(FILE *file, int chunk_size, struct LevelInfo *level)
477 {
478   int num_changed_custom_elements = getFile16BitBE(file);
479   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
480   int i;
481
482   if (chunk_size_expected != chunk_size)
483   {
484     ReadUnusedBytesFromFile(file, chunk_size - 2);
485     return chunk_size_expected;
486   }
487
488   for (i=0; i < num_changed_custom_elements; i++)
489   {
490     int element = getFile16BitBE(file);
491     int custom_target_element = getFile16BitBE(file);
492
493     if (IS_CUSTOM_ELEMENT(element))
494       element_info[element].change->target_element = custom_target_element;
495     else
496       Error(ERR_WARN, "invalid custom element number %d", element);
497   }
498
499   return chunk_size;
500 }
501
502 static int LoadLevel_CUS3(FILE *file, int chunk_size, struct LevelInfo *level)
503 {
504   int num_changed_custom_elements = getFile16BitBE(file);
505   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
506   int i, j, x, y;
507
508   if (chunk_size_expected != chunk_size)
509   {
510     ReadUnusedBytesFromFile(file, chunk_size - 2);
511     return chunk_size_expected;
512   }
513
514   for (i=0; i < num_changed_custom_elements; i++)
515   {
516     int element = getFile16BitBE(file);
517
518     if (!IS_CUSTOM_ELEMENT(element))
519     {
520       Error(ERR_WARN, "invalid custom element number %d", element);
521
522       element = EL_DEFAULT;     /* dummy element used for artwork config */
523     }
524
525     for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
526       element_info[element].description[j] = getFile8Bit(file);
527     element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
528
529     Properties[element][EP_BITFIELD_BASE] = getFile32BitBE(file);
530
531     /* some free bytes for future properties and padding */
532     ReadUnusedBytesFromFile(file, 7);
533
534     element_info[element].use_gfx_element = getFile8Bit(file);
535     element_info[element].gfx_element =
536       checkLevelElement(getFile16BitBE(file));
537
538     element_info[element].collect_score = getFile8Bit(file);
539     element_info[element].collect_count = getFile8Bit(file);
540
541     element_info[element].push_delay_fixed = getFile16BitBE(file);
542     element_info[element].push_delay_random = getFile16BitBE(file);
543     element_info[element].move_delay_fixed = getFile16BitBE(file);
544     element_info[element].move_delay_random = getFile16BitBE(file);
545
546     element_info[element].move_pattern = getFile16BitBE(file);
547     element_info[element].move_direction_initial = getFile8Bit(file);
548     element_info[element].move_stepsize = getFile8Bit(file);
549
550     for(y=0; y<3; y++)
551       for(x=0; x<3; x++)
552         element_info[element].content[x][y] =
553           checkLevelElement(getFile16BitBE(file));
554
555     element_info[element].change->events = getFile32BitBE(file);
556
557     element_info[element].change->target_element =
558       checkLevelElement(getFile16BitBE(file));
559
560     element_info[element].change->delay_fixed = getFile16BitBE(file);
561     element_info[element].change->delay_random = getFile16BitBE(file);
562     element_info[element].change->delay_frames = getFile16BitBE(file);
563
564     element_info[element].change->trigger_element =
565       checkLevelElement(getFile16BitBE(file));
566
567     element_info[element].change->explode = getFile8Bit(file);
568     element_info[element].change->use_content = getFile8Bit(file);
569     element_info[element].change->only_complete = getFile8Bit(file);
570     element_info[element].change->use_random_change = getFile8Bit(file);
571
572     element_info[element].change->random = getFile8Bit(file);
573     element_info[element].change->power = getFile8Bit(file);
574
575     for(y=0; y<3; y++)
576       for(x=0; x<3; x++)
577         element_info[element].change->content[x][y] =
578           checkLevelElement(getFile16BitBE(file));
579
580     element_info[element].slippery_type = getFile8Bit(file);
581
582     /* some free bytes for future properties and padding */
583     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
584
585     /* mark that this custom element has been modified */
586     element_info[element].modified_settings = TRUE;
587   }
588
589   return chunk_size;
590 }
591
592 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
593 {
594   char cookie[MAX_LINE_LEN];
595   char chunk_name[CHUNK_ID_LEN + 1];
596   int chunk_size;
597   FILE *file;
598
599   /* always start with reliable default values */
600   setLevelInfoToDefaults(level);
601
602   if (!(file = fopen(filename, MODE_READ)))
603   {
604     level->no_level_file = TRUE;
605
606     if (level != &level_template)
607       Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
608
609     return;
610   }
611
612   getFileChunkBE(file, chunk_name, NULL);
613   if (strcmp(chunk_name, "RND1") == 0)
614   {
615     getFile32BitBE(file);               /* not used */
616
617     getFileChunkBE(file, chunk_name, NULL);
618     if (strcmp(chunk_name, "CAVE") != 0)
619     {
620       Error(ERR_WARN, "unknown format of level file '%s'", filename);
621       fclose(file);
622       return;
623     }
624   }
625   else  /* check for pre-2.0 file format with cookie string */
626   {
627     strcpy(cookie, chunk_name);
628     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
629     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
630       cookie[strlen(cookie) - 1] = '\0';
631
632     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
633     {
634       Error(ERR_WARN, "unknown format of level file '%s'", filename);
635       fclose(file);
636       return;
637     }
638
639     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
640     {
641       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
642       fclose(file);
643       return;
644     }
645
646     /* pre-2.0 level files have no game version, so use file version here */
647     level->game_version = level->file_version;
648   }
649
650   if (level->file_version < FILE_VERSION_1_2)
651   {
652     /* level files from versions before 1.2.0 without chunk structure */
653     LoadLevel_HEAD(file, LEVEL_HEADER_SIZE,             level);
654     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
655   }
656   else
657   {
658     static struct
659     {
660       char *name;
661       int size;
662       int (*loader)(FILE *, int, struct LevelInfo *);
663     }
664     chunk_info[] =
665     {
666       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadLevel_VERS },
667       { "HEAD", LEVEL_HEADER_SIZE,      LoadLevel_HEAD },
668       { "AUTH", MAX_LEVEL_AUTHOR_LEN,   LoadLevel_AUTH },
669       { "BODY", -1,                     LoadLevel_BODY },
670       { "CONT", -1,                     LoadLevel_CONT },
671       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
672       { "CUS1", -1,                     LoadLevel_CUS1 },
673       { "CUS2", -1,                     LoadLevel_CUS2 },
674       { "CUS3", -1,                     LoadLevel_CUS3 },
675       {  NULL,  0,                      NULL }
676     };
677
678     while (getFileChunkBE(file, chunk_name, &chunk_size))
679     {
680       int i = 0;
681
682       while (chunk_info[i].name != NULL &&
683              strcmp(chunk_name, chunk_info[i].name) != 0)
684         i++;
685
686       if (chunk_info[i].name == NULL)
687       {
688         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
689               chunk_name, filename);
690         ReadUnusedBytesFromFile(file, chunk_size);
691       }
692       else if (chunk_info[i].size != -1 &&
693                chunk_info[i].size != chunk_size)
694       {
695         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
696               chunk_size, chunk_name, filename);
697         ReadUnusedBytesFromFile(file, chunk_size);
698       }
699       else
700       {
701         /* call function to load this level chunk */
702         int chunk_size_expected =
703           (chunk_info[i].loader)(file, chunk_size, level);
704
705         /* the size of some chunks cannot be checked before reading other
706            chunks first (like "HEAD" and "BODY") that contain some header
707            information, so check them here */
708         if (chunk_size_expected != chunk_size)
709         {
710           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
711                 chunk_size, chunk_name, filename);
712         }
713       }
714     }
715   }
716
717   fclose(file);
718 }
719
720 static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
721 {
722   int i, j, x, y;
723
724   if (leveldir_current == NULL)         /* only when dumping level */
725     return;
726
727   /* determine correct game engine version of current level */
728   if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
729       IS_LEVELCLASS_USER(leveldir_current))
730   {
731 #if 0
732     printf("\n::: This level is private or contributed: '%s'\n", filename);
733 #endif
734
735     /* For user contributed and private levels, use the version of
736        the game engine the levels were created for.
737        Since 2.0.1, the game engine version is now directly stored
738        in the level file (chunk "VERS"), so there is no need anymore
739        to set the game version from the file version (except for old,
740        pre-2.0 levels, where the game version is still taken from the
741        file format version used to store the level -- see above). */
742
743     /* do some special adjustments to support older level versions */
744     if (level->file_version == FILE_VERSION_1_0)
745     {
746       Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
747       Error(ERR_WARN, "using high speed movement for player");
748
749       /* player was faster than monsters in (pre-)1.0 levels */
750       level->double_speed = TRUE;
751     }
752
753     /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
754     if (level->game_version == VERSION_IDENT(2,0,1))
755       level->em_slippery_gems = TRUE;
756   }
757   else
758   {
759 #if 0
760     printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
761            leveldir_current->sort_priority, filename);
762 #endif
763
764     /* Always use the latest version of the game engine for all but
765        user contributed and private levels; this allows for actual
766        corrections in the game engine to take effect for existing,
767        converted levels (from "classic" or other existing games) to
768        make the game emulation more accurate, while (hopefully) not
769        breaking existing levels created from other players. */
770
771     level->game_version = GAME_VERSION_ACTUAL;
772
773     /* Set special EM style gems behaviour: EM style gems slip down from
774        normal, steel and growing wall. As this is a more fundamental change,
775        it seems better to set the default behaviour to "off" (as it is more
776        natural) and make it configurable in the level editor (as a property
777        of gem style elements). Already existing converted levels (neither
778        private nor contributed levels) are changed to the new behaviour. */
779
780     if (level->file_version < FILE_VERSION_2_0)
781       level->em_slippery_gems = TRUE;
782   }
783
784   /* map elements that have changed in newer versions */
785   for(y=0; y<level->fieldy; y++)
786   {
787     for(x=0; x<level->fieldx; x++)
788     {
789       int element = level->field[x][y];
790
791       if (level->game_version <= VERSION_IDENT(2,2,0))
792       {
793         /* map game font elements */
794         element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
795                    element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
796                    element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
797                    element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
798       }
799
800       if (level->game_version < VERSION_IDENT(3,0,0))
801       {
802         /* map Supaplex gravity tube elements */
803         element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
804                    element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
805                    element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
806                    element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
807                    element);
808       }
809
810       level->field[x][y] = element;
811     }
812   }
813
814   /* map custom element change events that have changed in newer versions
815      (these following values have accidentally changed in version 3.0.1) */
816   if (level->game_version <= VERSION_IDENT(3,0,0))
817   {
818     for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
819     {
820       int element = EL_CUSTOM_START + i;
821
822       /* order of checking events to be mapped is important */
823       for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
824       {
825         if (HAS_CHANGE_EVENT(element, j - 2))
826         {
827           SET_CHANGE_EVENT(element, j - 2, FALSE);
828           SET_CHANGE_EVENT(element, j, TRUE);
829         }
830       }
831
832       /* order of checking events to be mapped is important */
833       for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
834       {
835         if (HAS_CHANGE_EVENT(element, j - 1))
836         {
837           SET_CHANGE_EVENT(element, j - 1, FALSE);
838           SET_CHANGE_EVENT(element, j, TRUE);
839         }
840       }
841     }
842   }
843
844   /* copy elements to runtime playfield array */
845   for(x=0; x<MAX_LEV_FIELDX; x++)
846     for(y=0; y<MAX_LEV_FIELDY; y++)
847       Feld[x][y] = level->field[x][y];
848
849   /* initialize level size variables for faster access */
850   lev_fieldx = level->fieldx;
851   lev_fieldy = level->fieldy;
852
853   /* determine border element for this level */
854   SetBorderElement();
855
856   /* initialize element properties for level editor etc. */
857   InitElementPropertiesEngine(level->game_version);
858 }
859
860 void LoadLevelTemplate(int level_nr)
861 {
862   char *filename = getLevelFilename(level_nr);
863
864   LoadLevelFromFilename(&level_template, filename);
865
866   ActivateLevelTemplate();
867 }
868
869 void LoadLevel(int level_nr)
870 {
871   char *filename = getLevelFilename(level_nr);
872
873   LoadLevelFromFilename(&level, filename);
874
875   if (level.use_custom_template)
876     LoadLevelTemplate(-1);
877
878   LoadLevel_InitLevel(&level, filename);
879 }
880
881 static void SaveLevel_VERS(FILE *file, struct LevelInfo *level)
882 {
883   putFileVersion(file, level->file_version);
884   putFileVersion(file, level->game_version);
885 }
886
887 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
888 {
889   int i, x, y;
890
891   putFile8Bit(file, level->fieldx);
892   putFile8Bit(file, level->fieldy);
893
894   putFile16BitBE(file, level->time);
895   putFile16BitBE(file, level->gems_needed);
896
897   for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
898     putFile8Bit(file, level->name[i]);
899
900   for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
901     putFile8Bit(file, level->score[i]);
902
903   for(i=0; i<STD_ELEMENT_CONTENTS; i++)
904     for(y=0; y<3; y++)
905       for(x=0; x<3; x++)
906         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
907                            level->yamyam_content[i][x][y]));
908   putFile8Bit(file, level->amoeba_speed);
909   putFile8Bit(file, level->time_magic_wall);
910   putFile8Bit(file, level->time_wheel);
911   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
912                      level->amoeba_content));
913   putFile8Bit(file, (level->double_speed ? 1 : 0));
914   putFile8Bit(file, (level->gravity ? 1 : 0));
915   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
916   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
917
918   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
919
920   WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
921 }
922
923 static void SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
924 {
925   int i;
926
927   for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
928     putFile8Bit(file, level->author[i]);
929 }
930
931 static void SaveLevel_BODY(FILE *file, struct LevelInfo *level)
932 {
933   int x, y;
934
935   for(y=0; y<level->fieldy; y++) 
936     for(x=0; x<level->fieldx; x++) 
937       if (level->encoding_16bit_field)
938         putFile16BitBE(file, level->field[x][y]);
939       else
940         putFile8Bit(file, level->field[x][y]);
941 }
942
943 #if 0
944 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
945 {
946   int i, x, y;
947
948   putFile8Bit(file, EL_YAMYAM);
949   putFile8Bit(file, level->num_yamyam_contents);
950   putFile8Bit(file, 0);
951   putFile8Bit(file, 0);
952
953   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
954     for(y=0; y<3; y++)
955       for(x=0; x<3; x++)
956         if (level->encoding_16bit_field)
957           putFile16BitBE(file, level->yamyam_content[i][x][y]);
958         else
959           putFile8Bit(file, level->yamyam_content[i][x][y]);
960 }
961 #endif
962
963 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
964 {
965   int i, x, y;
966   int num_contents, content_xsize, content_ysize;
967   int content_array[MAX_ELEMENT_CONTENTS][3][3];
968
969   if (element == EL_YAMYAM)
970   {
971     num_contents = level->num_yamyam_contents;
972     content_xsize = 3;
973     content_ysize = 3;
974
975     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
976       for(y=0; y<3; y++)
977         for(x=0; x<3; x++)
978           content_array[i][x][y] = level->yamyam_content[i][x][y];
979   }
980   else if (element == EL_BD_AMOEBA)
981   {
982     num_contents = 1;
983     content_xsize = 1;
984     content_ysize = 1;
985
986     for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
987       for(y=0; y<3; y++)
988         for(x=0; x<3; x++)
989           content_array[i][x][y] = EL_EMPTY;
990     content_array[0][0][0] = level->amoeba_content;
991   }
992   else
993   {
994     /* chunk header already written -- write empty chunk data */
995     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
996
997     Error(ERR_WARN, "cannot save content for element '%d'", element);
998     return;
999   }
1000
1001   putFile16BitBE(file, element);
1002   putFile8Bit(file, num_contents);
1003   putFile8Bit(file, content_xsize);
1004   putFile8Bit(file, content_ysize);
1005
1006   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
1007
1008   for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
1009     for(y=0; y<3; y++)
1010       for(x=0; x<3; x++)
1011         putFile16BitBE(file, content_array[i][x][y]);
1012 }
1013
1014 #if 0
1015 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
1016                            int num_changed_custom_elements)
1017 {
1018   int i, check = 0;
1019
1020   putFile16BitBE(file, num_changed_custom_elements);
1021
1022   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1023   {
1024     int element = EL_CUSTOM_START + i;
1025
1026     if (Properties[element][EP_BITFIELD_BASE] != EP_BITMASK_DEFAULT)
1027     {
1028       if (check < num_changed_custom_elements)
1029       {
1030         putFile16BitBE(file, element);
1031         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1032       }
1033
1034       check++;
1035     }
1036   }
1037
1038   if (check != num_changed_custom_elements)     /* should not happen */
1039     Error(ERR_WARN, "inconsistent number of custom element properties");
1040 }
1041 #endif
1042
1043 #if 0
1044 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
1045                            int num_changed_custom_elements)
1046 {
1047   int i, check = 0;
1048
1049   putFile16BitBE(file, num_changed_custom_elements);
1050
1051   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1052   {
1053     int element = EL_CUSTOM_START + i;
1054
1055     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
1056     {
1057       if (check < num_changed_custom_elements)
1058       {
1059         putFile16BitBE(file, element);
1060         putFile16BitBE(file, element_info[element].change->target_element);
1061       }
1062
1063       check++;
1064     }
1065   }
1066
1067   if (check != num_changed_custom_elements)     /* should not happen */
1068     Error(ERR_WARN, "inconsistent number of custom target elements");
1069 }
1070 #endif
1071
1072 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
1073                            int num_changed_custom_elements)
1074 {
1075   int i, j, x, y, check = 0;
1076
1077   putFile16BitBE(file, num_changed_custom_elements);
1078
1079   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1080   {
1081     int element = EL_CUSTOM_START + i;
1082
1083     if (element_info[element].modified_settings)
1084     {
1085       if (check < num_changed_custom_elements)
1086       {
1087         putFile16BitBE(file, element);
1088
1089         for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
1090           putFile8Bit(file, element_info[element].description[j]);
1091
1092         putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
1093
1094         /* some free bytes for future properties and padding */
1095         WriteUnusedBytesToFile(file, 7);
1096
1097         putFile8Bit(file, element_info[element].use_gfx_element);
1098         putFile16BitBE(file, element_info[element].gfx_element);
1099
1100         putFile8Bit(file, element_info[element].collect_score);
1101         putFile8Bit(file, element_info[element].collect_count);
1102
1103         putFile16BitBE(file, element_info[element].push_delay_fixed);
1104         putFile16BitBE(file, element_info[element].push_delay_random);
1105         putFile16BitBE(file, element_info[element].move_delay_fixed);
1106         putFile16BitBE(file, element_info[element].move_delay_random);
1107
1108         putFile16BitBE(file, element_info[element].move_pattern);
1109         putFile8Bit(file, element_info[element].move_direction_initial);
1110         putFile8Bit(file, element_info[element].move_stepsize);
1111
1112         for(y=0; y<3; y++)
1113           for(x=0; x<3; x++)
1114             putFile16BitBE(file, element_info[element].content[x][y]);
1115
1116         putFile32BitBE(file, element_info[element].change->events);
1117
1118         putFile16BitBE(file, element_info[element].change->target_element);
1119
1120         putFile16BitBE(file, element_info[element].change->delay_fixed);
1121         putFile16BitBE(file, element_info[element].change->delay_random);
1122         putFile16BitBE(file, element_info[element].change->delay_frames);
1123
1124         putFile16BitBE(file, element_info[element].change->trigger_element);
1125
1126         putFile8Bit(file, element_info[element].change->explode);
1127         putFile8Bit(file, element_info[element].change->use_content);
1128         putFile8Bit(file, element_info[element].change->only_complete);
1129         putFile8Bit(file, element_info[element].change->use_random_change);
1130
1131         putFile8Bit(file, element_info[element].change->random);
1132         putFile8Bit(file, element_info[element].change->power);
1133
1134         for(y=0; y<3; y++)
1135           for(x=0; x<3; x++)
1136             putFile16BitBE(file, element_info[element].change->content[x][y]);
1137
1138         putFile8Bit(file, element_info[element].slippery_type);
1139
1140         /* some free bytes for future properties and padding */
1141         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
1142       }
1143
1144       check++;
1145     }
1146   }
1147
1148   if (check != num_changed_custom_elements)     /* should not happen */
1149     Error(ERR_WARN, "inconsistent number of custom element properties");
1150 }
1151
1152 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename)
1153 {
1154   int body_chunk_size;
1155   int num_changed_custom_elements = 0;
1156   int level_chunk_CUS3_size;
1157   int i, x, y;
1158   FILE *file;
1159
1160   if (!(file = fopen(filename, MODE_WRITE)))
1161   {
1162     Error(ERR_WARN, "cannot save level file '%s'", filename);
1163     return;
1164   }
1165
1166   level->file_version = FILE_VERSION_ACTUAL;
1167   level->game_version = GAME_VERSION_ACTUAL;
1168
1169   /* check level field for 16-bit elements */
1170   level->encoding_16bit_field = FALSE;
1171   for(y=0; y<level->fieldy; y++) 
1172     for(x=0; x<level->fieldx; x++) 
1173       if (level->field[x][y] > 255)
1174         level->encoding_16bit_field = TRUE;
1175
1176   /* check yamyam content for 16-bit elements */
1177   level->encoding_16bit_yamyam = FALSE;
1178   for(i=0; i<level->num_yamyam_contents; i++)
1179     for(y=0; y<3; y++)
1180       for(x=0; x<3; x++)
1181         if (level->yamyam_content[i][x][y] > 255)
1182           level->encoding_16bit_yamyam = TRUE;
1183
1184   /* check amoeba content for 16-bit elements */
1185   level->encoding_16bit_amoeba = FALSE;
1186   if (level->amoeba_content > 255)
1187     level->encoding_16bit_amoeba = TRUE;
1188
1189   /* calculate size of "BODY" chunk */
1190   body_chunk_size =
1191     level->fieldx * level->fieldy * (level->encoding_16bit_field ? 2 : 1);
1192
1193   /* check for non-standard custom elements and calculate "CUS3" chunk size */
1194   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
1195     if (element_info[EL_CUSTOM_START + i].modified_settings)
1196       num_changed_custom_elements++;
1197   level_chunk_CUS3_size = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
1198
1199   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1200   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
1201
1202   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1203   SaveLevel_VERS(file, level);
1204
1205   putFileChunkBE(file, "HEAD", LEVEL_HEADER_SIZE);
1206   SaveLevel_HEAD(file, level);
1207
1208   putFileChunkBE(file, "AUTH", MAX_LEVEL_AUTHOR_LEN);
1209   SaveLevel_AUTH(file, level);
1210
1211   putFileChunkBE(file, "BODY", body_chunk_size);
1212   SaveLevel_BODY(file, level);
1213
1214   if (level->encoding_16bit_yamyam ||
1215       level->num_yamyam_contents != STD_ELEMENT_CONTENTS)
1216   {
1217     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1218     SaveLevel_CNT2(file, level, EL_YAMYAM);
1219   }
1220
1221   if (level->encoding_16bit_amoeba)
1222   {
1223     putFileChunkBE(file, "CNT2", LEVEL_CHUNK_CNT2_SIZE);
1224     SaveLevel_CNT2(file, level, EL_BD_AMOEBA);
1225   }
1226
1227   if (num_changed_custom_elements > 0 && !level->use_custom_template)
1228   {
1229     putFileChunkBE(file, "CUS3", level_chunk_CUS3_size);
1230     SaveLevel_CUS3(file, level, num_changed_custom_elements);
1231   }
1232
1233   fclose(file);
1234
1235   SetFilePermissions(filename, PERMS_PRIVATE);
1236 }
1237
1238 void SaveLevel(int level_nr)
1239 {
1240   char *filename = getLevelFilename(level_nr);
1241
1242   SaveLevelFromFilename(&level, filename);
1243 }
1244
1245 void SaveLevelTemplate()
1246 {
1247   char *filename = getLevelFilename(-1);
1248
1249   SaveLevelFromFilename(&level, filename);
1250 }
1251
1252 void DumpLevel(struct LevelInfo *level)
1253 {
1254   printf_line("-", 79);
1255   printf("Level xxx (file version %08d, game version %08d)\n",
1256          level->file_version, level->game_version);
1257   printf_line("-", 79);
1258
1259   printf("Level Author: '%s'\n", level->author);
1260   printf("Level Title:  '%s'\n", level->name);
1261   printf("\n");
1262   printf("Playfield Size: %d x %d\n", level->fieldx, level->fieldy);
1263   printf("\n");
1264   printf("Level Time:  %d seconds\n", level->time);
1265   printf("Gems needed: %d\n", level->gems_needed);
1266   printf("\n");
1267   printf("Time for Magic Wall: %d seconds\n", level->time_magic_wall);
1268   printf("Time for Wheel:      %d seconds\n", level->time_wheel);
1269   printf("Time for Light:      %d seconds\n", level->time_light);
1270   printf("Time for Timegate:   %d seconds\n", level->time_timegate);
1271   printf("\n");
1272   printf("Amoeba Speed: %d\n", level->amoeba_speed);
1273   printf("\n");
1274   printf("Gravity:                %s\n", (level->gravity ? "yes" : "no"));
1275   printf("Double Speed Movement:  %s\n", (level->double_speed ? "yes" : "no"));
1276   printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
1277
1278   printf_line("-", 79);
1279 }
1280
1281
1282 /* ========================================================================= */
1283 /* tape file functions                                                       */
1284 /* ========================================================================= */
1285
1286 static void setTapeInfoToDefaults()
1287 {
1288   int i;
1289
1290   /* always start with reliable default values (empty tape) */
1291   TapeErase();
1292
1293   /* default values (also for pre-1.2 tapes) with only the first player */
1294   tape.player_participates[0] = TRUE;
1295   for(i=1; i<MAX_PLAYERS; i++)
1296     tape.player_participates[i] = FALSE;
1297
1298   /* at least one (default: the first) player participates in every tape */
1299   tape.num_participating_players = 1;
1300
1301   tape.level_nr = level_nr;
1302   tape.counter = 0;
1303   tape.changed = FALSE;
1304
1305   tape.recording = FALSE;
1306   tape.playing = FALSE;
1307   tape.pausing = FALSE;
1308 }
1309
1310 static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
1311 {
1312   tape->file_version = getFileVersion(file);
1313   tape->game_version = getFileVersion(file);
1314
1315   return chunk_size;
1316 }
1317
1318 static int LoadTape_HEAD(FILE *file, int chunk_size, struct TapeInfo *tape)
1319 {
1320   int i;
1321
1322   tape->random_seed = getFile32BitBE(file);
1323   tape->date        = getFile32BitBE(file);
1324   tape->length      = getFile32BitBE(file);
1325
1326   /* read header fields that are new since version 1.2 */
1327   if (tape->file_version >= FILE_VERSION_1_2)
1328   {
1329     byte store_participating_players = getFile8Bit(file);
1330     int engine_version;
1331
1332     /* since version 1.2, tapes store which players participate in the tape */
1333     tape->num_participating_players = 0;
1334     for(i=0; i<MAX_PLAYERS; i++)
1335     {
1336       tape->player_participates[i] = FALSE;
1337
1338       if (store_participating_players & (1 << i))
1339       {
1340         tape->player_participates[i] = TRUE;
1341         tape->num_participating_players++;
1342       }
1343     }
1344
1345     ReadUnusedBytesFromFile(file, TAPE_HEADER_UNUSED);
1346
1347     engine_version = getFileVersion(file);
1348     if (engine_version > 0)
1349       tape->engine_version = engine_version;
1350     else
1351       tape->engine_version = tape->game_version;
1352   }
1353
1354   return chunk_size;
1355 }
1356
1357 static int LoadTape_INFO(FILE *file, int chunk_size, struct TapeInfo *tape)
1358 {
1359   int level_identifier_size;
1360   int i;
1361
1362   level_identifier_size = getFile16BitBE(file);
1363
1364   tape->level_identifier =
1365     checked_realloc(tape->level_identifier, level_identifier_size);
1366
1367   for(i=0; i < level_identifier_size; i++)
1368     tape->level_identifier[i] = getFile8Bit(file);
1369
1370   tape->level_nr = getFile16BitBE(file);
1371
1372   chunk_size = 2 + level_identifier_size + 2;
1373
1374   return chunk_size;
1375 }
1376
1377 static int LoadTape_BODY(FILE *file, int chunk_size, struct TapeInfo *tape)
1378 {
1379   int i, j;
1380   int chunk_size_expected =
1381     (tape->num_participating_players + 1) * tape->length;
1382
1383   if (chunk_size_expected != chunk_size)
1384   {
1385     ReadUnusedBytesFromFile(file, chunk_size);
1386     return chunk_size_expected;
1387   }
1388
1389   for(i=0; i<tape->length; i++)
1390   {
1391     if (i >= MAX_TAPELEN)
1392       break;
1393
1394     for(j=0; j<MAX_PLAYERS; j++)
1395     {
1396       tape->pos[i].action[j] = MV_NO_MOVING;
1397
1398       if (tape->player_participates[j])
1399         tape->pos[i].action[j] = getFile8Bit(file);
1400     }
1401
1402     tape->pos[i].delay = getFile8Bit(file);
1403
1404     if (tape->file_version == FILE_VERSION_1_0)
1405     {
1406       /* eliminate possible diagonal moves in old tapes */
1407       /* this is only for backward compatibility */
1408
1409       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
1410       byte action = tape->pos[i].action[0];
1411       int k, num_moves = 0;
1412
1413       for (k=0; k<4; k++)
1414       {
1415         if (action & joy_dir[k])
1416         {
1417           tape->pos[i + num_moves].action[0] = joy_dir[k];
1418           if (num_moves > 0)
1419             tape->pos[i + num_moves].delay = 0;
1420           num_moves++;
1421         }
1422       }
1423
1424       if (num_moves > 1)
1425       {
1426         num_moves--;
1427         i += num_moves;
1428         tape->length += num_moves;
1429       }
1430     }
1431     else if (tape->file_version < FILE_VERSION_2_0)
1432     {
1433       /* convert pre-2.0 tapes to new tape format */
1434
1435       if (tape->pos[i].delay > 1)
1436       {
1437         /* action part */
1438         tape->pos[i + 1] = tape->pos[i];
1439         tape->pos[i + 1].delay = 1;
1440
1441         /* delay part */
1442         for(j=0; j<MAX_PLAYERS; j++)
1443           tape->pos[i].action[j] = MV_NO_MOVING;
1444         tape->pos[i].delay--;
1445
1446         i++;
1447         tape->length++;
1448       }
1449     }
1450
1451     if (feof(file))
1452       break;
1453   }
1454
1455   if (i != tape->length)
1456     chunk_size = (tape->num_participating_players + 1) * i;
1457
1458   return chunk_size;
1459 }
1460
1461 void LoadTapeFromFilename(char *filename)
1462 {
1463   char cookie[MAX_LINE_LEN];
1464   char chunk_name[CHUNK_ID_LEN + 1];
1465   FILE *file;
1466   int chunk_size;
1467
1468   /* always start with reliable default values */
1469   setTapeInfoToDefaults();
1470
1471   if (!(file = fopen(filename, MODE_READ)))
1472     return;
1473
1474   getFileChunkBE(file, chunk_name, NULL);
1475   if (strcmp(chunk_name, "RND1") == 0)
1476   {
1477     getFile32BitBE(file);               /* not used */
1478
1479     getFileChunkBE(file, chunk_name, NULL);
1480     if (strcmp(chunk_name, "TAPE") != 0)
1481     {
1482       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1483       fclose(file);
1484       return;
1485     }
1486   }
1487   else  /* check for pre-2.0 file format with cookie string */
1488   {
1489     strcpy(cookie, chunk_name);
1490     fgets(&cookie[4], MAX_LINE_LEN - 4, file);
1491     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1492       cookie[strlen(cookie) - 1] = '\0';
1493
1494     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
1495     {
1496       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
1497       fclose(file);
1498       return;
1499     }
1500
1501     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
1502     {
1503       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
1504       fclose(file);
1505       return;
1506     }
1507
1508     /* pre-2.0 tape files have no game version, so use file version here */
1509     tape.game_version = tape.file_version;
1510   }
1511
1512   if (tape.file_version < FILE_VERSION_1_2)
1513   {
1514     /* tape files from versions before 1.2.0 without chunk structure */
1515     LoadTape_HEAD(file, TAPE_HEADER_SIZE, &tape);
1516     LoadTape_BODY(file, 2 * tape.length,  &tape);
1517   }
1518   else
1519   {
1520     static struct
1521     {
1522       char *name;
1523       int size;
1524       int (*loader)(FILE *, int, struct TapeInfo *);
1525     }
1526     chunk_info[] =
1527     {
1528       { "VERS", FILE_VERS_CHUNK_SIZE,   LoadTape_VERS },
1529       { "HEAD", TAPE_HEADER_SIZE,       LoadTape_HEAD },
1530       { "INFO", -1,                     LoadTape_INFO },
1531       { "BODY", -1,                     LoadTape_BODY },
1532       {  NULL,  0,                      NULL }
1533     };
1534
1535     while (getFileChunkBE(file, chunk_name, &chunk_size))
1536     {
1537       int i = 0;
1538
1539       while (chunk_info[i].name != NULL &&
1540              strcmp(chunk_name, chunk_info[i].name) != 0)
1541         i++;
1542
1543       if (chunk_info[i].name == NULL)
1544       {
1545         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
1546               chunk_name, filename);
1547         ReadUnusedBytesFromFile(file, chunk_size);
1548       }
1549       else if (chunk_info[i].size != -1 &&
1550                chunk_info[i].size != chunk_size)
1551       {
1552         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1553               chunk_size, chunk_name, filename);
1554         ReadUnusedBytesFromFile(file, chunk_size);
1555       }
1556       else
1557       {
1558         /* call function to load this tape chunk */
1559         int chunk_size_expected =
1560           (chunk_info[i].loader)(file, chunk_size, &tape);
1561
1562         /* the size of some chunks cannot be checked before reading other
1563            chunks first (like "HEAD" and "BODY") that contain some header
1564            information, so check them here */
1565         if (chunk_size_expected != chunk_size)
1566         {
1567           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
1568                 chunk_size, chunk_name, filename);
1569         }
1570       }
1571     }
1572   }
1573
1574   fclose(file);
1575
1576   tape.length_seconds = GetTapeLength();
1577
1578 #if 0
1579   printf("tape game version: %d\n", tape.game_version);
1580   printf("tape engine version: %d\n", tape.engine_version);
1581 #endif
1582 }
1583
1584 void LoadTape(int level_nr)
1585 {
1586   char *filename = getTapeFilename(level_nr);
1587
1588   LoadTapeFromFilename(filename);
1589 }
1590
1591 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
1592 {
1593   putFileVersion(file, tape->file_version);
1594   putFileVersion(file, tape->game_version);
1595 }
1596
1597 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
1598 {
1599   int i;
1600   byte store_participating_players = 0;
1601
1602   /* set bits for participating players for compact storage */
1603   for(i=0; i<MAX_PLAYERS; i++)
1604     if (tape->player_participates[i])
1605       store_participating_players |= (1 << i);
1606
1607   putFile32BitBE(file, tape->random_seed);
1608   putFile32BitBE(file, tape->date);
1609   putFile32BitBE(file, tape->length);
1610
1611   putFile8Bit(file, store_participating_players);
1612
1613   /* unused bytes not at the end here for 4-byte alignment of engine_version */
1614   WriteUnusedBytesToFile(file, TAPE_HEADER_UNUSED);
1615
1616   putFileVersion(file, tape->engine_version);
1617 }
1618
1619 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
1620 {
1621   int level_identifier_size = strlen(tape->level_identifier) + 1;
1622   int i;
1623
1624   putFile16BitBE(file, level_identifier_size);
1625
1626   for(i=0; i < level_identifier_size; i++)
1627     putFile8Bit(file, tape->level_identifier[i]);
1628
1629   putFile16BitBE(file, tape->level_nr);
1630 }
1631
1632 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
1633 {
1634   int i, j;
1635
1636   for(i=0; i<tape->length; i++)
1637   {
1638     for(j=0; j<MAX_PLAYERS; j++)
1639       if (tape->player_participates[j])
1640         putFile8Bit(file, tape->pos[i].action[j]);
1641
1642     putFile8Bit(file, tape->pos[i].delay);
1643   }
1644 }
1645
1646 void SaveTape(int level_nr)
1647 {
1648   char *filename = getTapeFilename(level_nr);
1649   FILE *file;
1650   boolean new_tape = TRUE;
1651   int num_participating_players = 0;
1652   int info_chunk_size;
1653   int body_chunk_size;
1654   int i;
1655
1656   InitTapeDirectory(leveldir_current->filename);
1657
1658   /* if a tape still exists, ask to overwrite it */
1659   if (access(filename, F_OK) == 0)
1660   {
1661     new_tape = FALSE;
1662     if (!Request("Replace old tape ?", REQ_ASK))
1663       return;
1664   }
1665
1666   if (!(file = fopen(filename, MODE_WRITE)))
1667   {
1668     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
1669     return;
1670   }
1671
1672   tape.file_version = FILE_VERSION_ACTUAL;
1673   tape.game_version = GAME_VERSION_ACTUAL;
1674
1675   /* count number of participating players  */
1676   for(i=0; i<MAX_PLAYERS; i++)
1677     if (tape.player_participates[i])
1678       num_participating_players++;
1679
1680   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
1681   body_chunk_size = (num_participating_players + 1) * tape.length;
1682
1683   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
1684   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
1685
1686   putFileChunkBE(file, "VERS", FILE_VERS_CHUNK_SIZE);
1687   SaveTape_VERS(file, &tape);
1688
1689   putFileChunkBE(file, "HEAD", TAPE_HEADER_SIZE);
1690   SaveTape_HEAD(file, &tape);
1691
1692   putFileChunkBE(file, "INFO", info_chunk_size);
1693   SaveTape_INFO(file, &tape);
1694
1695   putFileChunkBE(file, "BODY", body_chunk_size);
1696   SaveTape_BODY(file, &tape);
1697
1698   fclose(file);
1699
1700   SetFilePermissions(filename, PERMS_PRIVATE);
1701
1702   tape.changed = FALSE;
1703
1704   if (new_tape)
1705     Request("tape saved !", REQ_CONFIRM);
1706 }
1707
1708 void DumpTape(struct TapeInfo *tape)
1709 {
1710   int i, j;
1711
1712   if (TAPE_IS_EMPTY(*tape))
1713   {
1714     Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
1715     return;
1716   }
1717
1718   printf_line("-", 79);
1719   printf("Tape of Level %03d (file version %08d, game version %08d)\n",
1720          tape->level_nr, tape->file_version, tape->game_version);
1721   printf("Level series identifier: '%s'\n", tape->level_identifier);
1722   printf_line("-", 79);
1723
1724   for(i=0; i<tape->length; i++)
1725   {
1726     if (i >= MAX_TAPELEN)
1727       break;
1728
1729     printf("%03d: ", i);
1730
1731     for(j=0; j<MAX_PLAYERS; j++)
1732     {
1733       if (tape->player_participates[j])
1734       {
1735         int action = tape->pos[i].action[j];
1736
1737         printf("%d:%02x ", j, action);
1738         printf("[%c%c%c%c|%c%c] - ",
1739                (action & JOY_LEFT ? '<' : ' '),
1740                (action & JOY_RIGHT ? '>' : ' '),
1741                (action & JOY_UP ? '^' : ' '),
1742                (action & JOY_DOWN ? 'v' : ' '),
1743                (action & JOY_BUTTON_1 ? '1' : ' '),
1744                (action & JOY_BUTTON_2 ? '2' : ' '));
1745       }
1746     }
1747
1748     printf("(%03d)\n", tape->pos[i].delay);
1749   }
1750
1751   printf_line("-", 79);
1752 }
1753
1754
1755 /* ========================================================================= */
1756 /* score file functions                                                      */
1757 /* ========================================================================= */
1758
1759 void LoadScore(int level_nr)
1760 {
1761   int i;
1762   char *filename = getScoreFilename(level_nr);
1763   char cookie[MAX_LINE_LEN];
1764   char line[MAX_LINE_LEN];
1765   char *line_ptr;
1766   FILE *file;
1767
1768   /* always start with reliable default values */
1769   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1770   {
1771     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
1772     highscore[i].Score = 0;
1773   }
1774
1775   if (!(file = fopen(filename, MODE_READ)))
1776     return;
1777
1778   /* check file identifier */
1779   fgets(cookie, MAX_LINE_LEN, file);
1780   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
1781     cookie[strlen(cookie) - 1] = '\0';
1782
1783   if (!checkCookieString(cookie, SCORE_COOKIE))
1784   {
1785     Error(ERR_WARN, "unknown format of score file '%s'", filename);
1786     fclose(file);
1787     return;
1788   }
1789
1790   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1791   {
1792     fscanf(file, "%d", &highscore[i].Score);
1793     fgets(line, MAX_LINE_LEN, file);
1794
1795     if (line[strlen(line) - 1] == '\n')
1796       line[strlen(line) - 1] = '\0';
1797
1798     for (line_ptr = line; *line_ptr; line_ptr++)
1799     {
1800       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
1801       {
1802         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
1803         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
1804         break;
1805       }
1806     }
1807   }
1808
1809   fclose(file);
1810 }
1811
1812 void SaveScore(int level_nr)
1813 {
1814   int i;
1815   char *filename = getScoreFilename(level_nr);
1816   FILE *file;
1817
1818   InitScoreDirectory(leveldir_current->filename);
1819
1820   if (!(file = fopen(filename, MODE_WRITE)))
1821   {
1822     Error(ERR_WARN, "cannot save score for level %d", level_nr);
1823     return;
1824   }
1825
1826   fprintf(file, "%s\n\n", SCORE_COOKIE);
1827
1828   for(i=0; i<MAX_SCORE_ENTRIES; i++)
1829     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
1830
1831   fclose(file);
1832
1833   SetFilePermissions(filename, PERMS_PUBLIC);
1834 }
1835
1836
1837 /* ========================================================================= */
1838 /* setup file functions                                                      */
1839 /* ========================================================================= */
1840
1841 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
1842
1843 /* global setup */
1844 #define SETUP_TOKEN_PLAYER_NAME                 0
1845 #define SETUP_TOKEN_SOUND                       1
1846 #define SETUP_TOKEN_SOUND_LOOPS                 2
1847 #define SETUP_TOKEN_SOUND_MUSIC                 3
1848 #define SETUP_TOKEN_SOUND_SIMPLE                4
1849 #define SETUP_TOKEN_TOONS                       5
1850 #define SETUP_TOKEN_SCROLL_DELAY                6
1851 #define SETUP_TOKEN_SOFT_SCROLLING              7
1852 #define SETUP_TOKEN_FADING                      8
1853 #define SETUP_TOKEN_AUTORECORD                  9
1854 #define SETUP_TOKEN_QUICK_DOORS                 10
1855 #define SETUP_TOKEN_TEAM_MODE                   11
1856 #define SETUP_TOKEN_HANDICAP                    12
1857 #define SETUP_TOKEN_TIME_LIMIT                  13
1858 #define SETUP_TOKEN_FULLSCREEN                  14
1859 #define SETUP_TOKEN_ASK_ON_ESCAPE               15
1860 #define SETUP_TOKEN_GRAPHICS_SET                16
1861 #define SETUP_TOKEN_SOUNDS_SET                  17
1862 #define SETUP_TOKEN_MUSIC_SET                   18
1863 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     19
1864 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       20
1865 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        21
1866
1867 #define NUM_GLOBAL_SETUP_TOKENS                 22
1868
1869 /* editor setup */
1870 #define SETUP_TOKEN_EDITOR_EL_BOULDERDASH       0
1871 #define SETUP_TOKEN_EDITOR_EL_EMERALD_MINE      1
1872 #define SETUP_TOKEN_EDITOR_EL_MORE              2
1873 #define SETUP_TOKEN_EDITOR_EL_SOKOBAN           3
1874 #define SETUP_TOKEN_EDITOR_EL_SUPAPLEX          4
1875 #define SETUP_TOKEN_EDITOR_EL_DIAMOND_CAVES     5
1876 #define SETUP_TOKEN_EDITOR_EL_DX_BOULDERDASH    6
1877 #define SETUP_TOKEN_EDITOR_EL_CHARS             7
1878 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            8
1879
1880 #define NUM_EDITOR_SETUP_TOKENS                 9
1881
1882 /* shortcut setup */
1883 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
1884 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
1885 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
1886
1887 #define NUM_SHORTCUT_SETUP_TOKENS               3
1888
1889 /* player setup */
1890 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
1891 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
1892 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
1893 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
1894 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
1895 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
1896 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
1897 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
1898 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
1899 #define SETUP_TOKEN_PLAYER_JOY_BOMB             9
1900 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
1901 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
1902 #define SETUP_TOKEN_PLAYER_KEY_UP               12
1903 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
1904 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
1905 #define SETUP_TOKEN_PLAYER_KEY_BOMB             15
1906
1907 #define NUM_PLAYER_SETUP_TOKENS                 16
1908
1909 /* system setup */
1910 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      0
1911 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  1
1912
1913 #define NUM_SYSTEM_SETUP_TOKENS                 2
1914
1915 /* options setup */
1916 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
1917
1918 #define NUM_OPTIONS_SETUP_TOKENS                1
1919
1920
1921 static struct SetupInfo si;
1922 static struct SetupEditorInfo sei;
1923 static struct SetupShortcutInfo ssi;
1924 static struct SetupInputInfo sii;
1925 static struct SetupSystemInfo syi;
1926 static struct OptionInfo soi;
1927
1928 static struct TokenInfo global_setup_tokens[] =
1929 {
1930   { TYPE_STRING, &si.player_name,       "player_name"                   },
1931   { TYPE_SWITCH, &si.sound,             "sound"                         },
1932   { TYPE_SWITCH, &si.sound_loops,       "repeating_sound_loops"         },
1933   { TYPE_SWITCH, &si.sound_music,       "background_music"              },
1934   { TYPE_SWITCH, &si.sound_simple,      "simple_sound_effects"          },
1935   { TYPE_SWITCH, &si.toons,             "toons"                         },
1936   { TYPE_SWITCH, &si.scroll_delay,      "scroll_delay"                  },
1937   { TYPE_SWITCH, &si.soft_scrolling,    "soft_scrolling"                },
1938   { TYPE_SWITCH, &si.fading,            "screen_fading"                 },
1939   { TYPE_SWITCH, &si.autorecord,        "automatic_tape_recording"      },
1940   { TYPE_SWITCH, &si.quick_doors,       "quick_doors"                   },
1941   { TYPE_SWITCH, &si.team_mode,         "team_mode"                     },
1942   { TYPE_SWITCH, &si.handicap,          "handicap"                      },
1943   { TYPE_SWITCH, &si.time_limit,        "time_limit"                    },
1944   { TYPE_SWITCH, &si.fullscreen,        "fullscreen"                    },
1945   { TYPE_SWITCH, &si.ask_on_escape,     "ask_on_escape"                 },
1946   { TYPE_STRING, &si.graphics_set,      "graphics_set"                  },
1947   { TYPE_STRING, &si.sounds_set,        "sounds_set"                    },
1948   { TYPE_STRING, &si.music_set,         "music_set"                     },
1949   { TYPE_SWITCH, &si.override_level_graphics, "override_level_graphics" },
1950   { TYPE_SWITCH, &si.override_level_sounds,   "override_level_sounds"   },
1951   { TYPE_SWITCH, &si.override_level_music,    "override_level_music"    },
1952 };
1953
1954 static struct TokenInfo editor_setup_tokens[] =
1955 {
1956   { TYPE_SWITCH, &sei.el_boulderdash,   "editor.el_boulderdash"         },
1957   { TYPE_SWITCH, &sei.el_emerald_mine,  "editor.el_emerald_mine"        },
1958   { TYPE_SWITCH, &sei.el_more,          "editor.el_more"                },
1959   { TYPE_SWITCH, &sei.el_sokoban,       "editor.el_sokoban"             },
1960   { TYPE_SWITCH, &sei.el_supaplex,      "editor.el_supaplex"            },
1961   { TYPE_SWITCH, &sei.el_diamond_caves, "editor.el_diamond_caves"       },
1962   { TYPE_SWITCH, &sei.el_dx_boulderdash,"editor.el_dx_boulderdash"      },
1963   { TYPE_SWITCH, &sei.el_chars,         "editor.el_chars"               },
1964   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
1965   { TYPE_SWITCH, &sei.el_custom_more,   "editor.el_custom_more"         },
1966 };
1967
1968 static struct TokenInfo shortcut_setup_tokens[] =
1969 {
1970   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
1971   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
1972   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         }
1973 };
1974
1975 static struct TokenInfo player_setup_tokens[] =
1976 {
1977   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
1978   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
1979   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
1980   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
1981   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
1982   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
1983   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
1984   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
1985   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
1986   { TYPE_INTEGER, &sii.joy.bomb,        ".joy.place_bomb"               },
1987   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
1988   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
1989   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
1990   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
1991   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
1992   { TYPE_KEY_X11, &sii.key.bomb,        ".key.place_bomb"               }
1993 };
1994
1995 static struct TokenInfo system_setup_tokens[] =
1996 {
1997   { TYPE_STRING,  &syi.sdl_audiodriver, "system.sdl_audiodriver"        },
1998   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" }
1999 };
2000
2001 static struct TokenInfo options_setup_tokens[] =
2002 {
2003   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               }
2004 };
2005
2006 static char *get_corrected_login_name(char *login_name)
2007 {
2008   /* needed because player name must be a fixed length string */
2009   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
2010
2011   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
2012   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
2013
2014   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
2015     if (strchr(login_name_new, ' '))
2016       *strchr(login_name_new, ' ') = '\0';
2017
2018   return login_name_new;
2019 }
2020
2021 static void setSetupInfoToDefaults(struct SetupInfo *si)
2022 {
2023   int i;
2024
2025   si->player_name = get_corrected_login_name(getLoginName());
2026
2027   si->sound = TRUE;
2028   si->sound_loops = TRUE;
2029   si->sound_music = TRUE;
2030   si->sound_simple = TRUE;
2031   si->toons = TRUE;
2032   si->double_buffering = TRUE;
2033   si->direct_draw = !si->double_buffering;
2034   si->scroll_delay = TRUE;
2035   si->soft_scrolling = TRUE;
2036   si->fading = FALSE;
2037   si->autorecord = TRUE;
2038   si->quick_doors = FALSE;
2039   si->team_mode = FALSE;
2040   si->handicap = TRUE;
2041   si->time_limit = TRUE;
2042   si->fullscreen = FALSE;
2043   si->ask_on_escape = TRUE;
2044
2045   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
2046   si->sounds_set = getStringCopy(SND_CLASSIC_SUBDIR);
2047   si->music_set = getStringCopy(MUS_CLASSIC_SUBDIR);
2048   si->override_level_graphics = FALSE;
2049   si->override_level_sounds = FALSE;
2050   si->override_level_music = FALSE;
2051
2052   si->editor.el_boulderdash = TRUE;
2053   si->editor.el_emerald_mine = TRUE;
2054   si->editor.el_more = TRUE;
2055   si->editor.el_sokoban = TRUE;
2056   si->editor.el_supaplex = TRUE;
2057   si->editor.el_diamond_caves = TRUE;
2058   si->editor.el_dx_boulderdash = TRUE;
2059   si->editor.el_chars = TRUE;
2060   si->editor.el_custom = TRUE;
2061   si->editor.el_custom_more = FALSE;
2062
2063   si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
2064   si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
2065   si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
2066
2067   for (i=0; i<MAX_PLAYERS; i++)
2068   {
2069     si->input[i].use_joystick = FALSE;
2070     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
2071     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
2072     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
2073     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
2074     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
2075     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
2076     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
2077     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
2078     si->input[i].joy.bomb  = (i == 0 ? JOY_BUTTON_2 : 0);
2079     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
2080     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
2081     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
2082     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
2083     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
2084     si->input[i].key.bomb  = (i == 0 ? DEFAULT_KEY_BOMB  : KSYM_UNDEFINED);
2085   }
2086
2087   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
2088   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
2089
2090   si->options.verbose = FALSE;
2091 }
2092
2093 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
2094 {
2095   int i, pnr;
2096
2097   if (!setup_file_hash)
2098     return;
2099
2100   /* global setup */
2101   si = setup;
2102   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2103     setSetupInfo(global_setup_tokens, i,
2104                  getHashEntry(setup_file_hash, global_setup_tokens[i].text));
2105   setup = si;
2106
2107   /* editor setup */
2108   sei = setup.editor;
2109   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2110     setSetupInfo(editor_setup_tokens, i,
2111                  getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
2112   setup.editor = sei;
2113
2114   /* shortcut setup */
2115   ssi = setup.shortcut;
2116   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2117     setSetupInfo(shortcut_setup_tokens, i,
2118                  getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
2119   setup.shortcut = ssi;
2120
2121   /* player setup */
2122   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2123   {
2124     char prefix[30];
2125
2126     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2127
2128     sii = setup.input[pnr];
2129     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2130     {
2131       char full_token[100];
2132
2133       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
2134       setSetupInfo(player_setup_tokens, i,
2135                    getHashEntry(setup_file_hash, full_token));
2136     }
2137     setup.input[pnr] = sii;
2138   }
2139
2140   /* system setup */
2141   syi = setup.system;
2142   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2143     setSetupInfo(system_setup_tokens, i,
2144                  getHashEntry(setup_file_hash, system_setup_tokens[i].text));
2145   setup.system = syi;
2146
2147   /* options setup */
2148   soi = setup.options;
2149   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2150     setSetupInfo(options_setup_tokens, i,
2151                  getHashEntry(setup_file_hash, options_setup_tokens[i].text));
2152   setup.options = soi;
2153 }
2154
2155 void LoadSetup()
2156 {
2157   char *filename = getSetupFilename();
2158   SetupFileHash *setup_file_hash = NULL;
2159
2160   /* always start with reliable default values */
2161   setSetupInfoToDefaults(&setup);
2162
2163   setup_file_hash = loadSetupFileHash(filename);
2164
2165   if (setup_file_hash)
2166   {
2167     char *player_name_new;
2168
2169     checkSetupFileHashIdentifier(setup_file_hash, getCookie("SETUP"));
2170     decodeSetupFileHash(setup_file_hash);
2171
2172     setup.direct_draw = !setup.double_buffering;
2173
2174     freeSetupFileHash(setup_file_hash);
2175
2176     /* needed to work around problems with fixed length strings */
2177     player_name_new = get_corrected_login_name(setup.player_name);
2178     free(setup.player_name);
2179     setup.player_name = player_name_new;
2180   }
2181   else
2182     Error(ERR_WARN, "using default setup values");
2183 }
2184
2185 void SaveSetup()
2186 {
2187   char *filename = getSetupFilename();
2188   FILE *file;
2189   int i, pnr;
2190
2191   InitUserDataDirectory();
2192
2193   if (!(file = fopen(filename, MODE_WRITE)))
2194   {
2195     Error(ERR_WARN, "cannot write setup file '%s'", filename);
2196     return;
2197   }
2198
2199   fprintf(file, "%s\n", getFormattedSetupEntry(TOKEN_STR_FILE_IDENTIFIER,
2200                                                getCookie("SETUP")));
2201   fprintf(file, "\n");
2202
2203   /* global setup */
2204   si = setup;
2205   for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
2206   {
2207     /* just to make things nicer :) */
2208     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
2209         i == SETUP_TOKEN_GRAPHICS_SET)
2210       fprintf(file, "\n");
2211
2212     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
2213   }
2214
2215   /* editor setup */
2216   sei = setup.editor;
2217   fprintf(file, "\n");
2218   for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
2219     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
2220
2221   /* shortcut setup */
2222   ssi = setup.shortcut;
2223   fprintf(file, "\n");
2224   for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
2225     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
2226
2227   /* player setup */
2228   for (pnr=0; pnr<MAX_PLAYERS; pnr++)
2229   {
2230     char prefix[30];
2231
2232     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
2233     fprintf(file, "\n");
2234
2235     sii = setup.input[pnr];
2236     for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
2237       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
2238   }
2239
2240   /* system setup */
2241   syi = setup.system;
2242   fprintf(file, "\n");
2243   for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
2244     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
2245
2246   /* options setup */
2247   soi = setup.options;
2248   fprintf(file, "\n");
2249   for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
2250     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
2251
2252   fclose(file);
2253
2254   SetFilePermissions(filename, PERMS_PRIVATE);
2255 }
2256
2257 void LoadCustomElementDescriptions()
2258 {
2259   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2260   SetupFileHash *setup_file_hash;
2261   int i;
2262
2263   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2264   {
2265     if (element_info[i].custom_description != NULL)
2266     {
2267       free(element_info[i].custom_description);
2268       element_info[i].custom_description = NULL;
2269     }
2270   }
2271
2272   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2273     return;
2274
2275   for (i=0; i<NUM_FILE_ELEMENTS; i++)
2276   {
2277     char *token = getStringCat2(element_info[i].token_name, ".name");
2278     char *value = getHashEntry(setup_file_hash, token);
2279
2280     if (value != NULL)
2281       element_info[i].custom_description = getStringCopy(value);
2282
2283     free(token);
2284   }
2285
2286   freeSetupFileHash(setup_file_hash);
2287 }
2288
2289 void LoadSpecialMenuDesignSettings()
2290 {
2291   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
2292   SetupFileHash *setup_file_hash;
2293   int i, j;
2294
2295   /* always start with reliable default values from default config */
2296   for (i=0; image_config_vars[i].token != NULL; i++)
2297     for (j=0; image_config[j].token != NULL; j++)
2298       if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
2299         *image_config_vars[i].value =
2300           get_integer_from_string(image_config[j].value);
2301
2302   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
2303     return;
2304
2305   /* special case: initialize with default values that may be overwritten */
2306   for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
2307   {
2308     char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
2309     char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
2310     char *list_size = getHashEntry(setup_file_hash, "menu.list_size");
2311
2312     if (value_x != NULL)
2313       menu.draw_xoffset[i] = get_integer_from_string(value_x);
2314     if (value_y != NULL)
2315       menu.draw_yoffset[i] = get_integer_from_string(value_y);
2316     if (list_size != NULL)
2317       menu.list_size[i] = get_integer_from_string(list_size);
2318   }
2319
2320   /* read (and overwrite with) values that may be specified in config file */
2321   for (i=0; image_config_vars[i].token != NULL; i++)
2322   {
2323     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
2324
2325     if (value != NULL)
2326       *image_config_vars[i].value = get_integer_from_string(value);
2327   }
2328
2329   freeSetupFileHash(setup_file_hash);
2330 }