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