changed listing all invalid SP level file elements only in debug mode
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.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 #include "config.h"
24
25 #define ENABLE_UNUSED_CODE      0       /* currently unused functions */
26 #define ENABLE_HISTORIC_CHUNKS  0       /* only for historic reference */
27 #define ENABLE_RESERVED_CODE    0       /* reserved for later use */
28
29 #define CHUNK_ID_LEN            4       /* IFF style chunk id length  */
30 #define CHUNK_SIZE_UNDEFINED    0       /* undefined chunk size == 0  */
31 #define CHUNK_SIZE_NONE         -1      /* do not write chunk size    */
32
33 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
34 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
35
36 #define LEVEL_CHUNK_VERS_SIZE   8       /* size of file version chunk */
37 #define LEVEL_CHUNK_DATE_SIZE   4       /* size of file date chunk    */
38 #define LEVEL_CHUNK_HEAD_SIZE   80      /* size of level file header  */
39 #define LEVEL_CHUNK_HEAD_UNUSED 0       /* unused level header bytes  */
40 #define LEVEL_CHUNK_CNT2_SIZE   160     /* size of level CNT2 chunk   */
41 #define LEVEL_CHUNK_CNT2_UNUSED 11      /* unused CNT2 chunk bytes    */
42 #define LEVEL_CHUNK_CNT3_HEADER 16      /* size of level CNT3 header  */
43 #define LEVEL_CHUNK_CNT3_UNUSED 10      /* unused CNT3 chunk bytes    */
44 #define LEVEL_CPART_CUS3_SIZE   134     /* size of CUS3 chunk part    */
45 #define LEVEL_CPART_CUS3_UNUSED 15      /* unused CUS3 bytes / part   */
46 #define LEVEL_CHUNK_GRP1_SIZE   74      /* size of level GRP1 chunk   */
47
48 /* (element number, number of change pages, change page number) */
49 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
50
51 /* (element number only) */
52 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
53 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
54
55 /* (nothing at all if unchanged) */
56 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
57
58 #define TAPE_CHUNK_VERS_SIZE    8       /* size of file version chunk */
59 #define TAPE_CHUNK_HEAD_SIZE    20      /* size of tape file header   */
60 #define TAPE_CHUNK_HEAD_UNUSED  3       /* unused tape header bytes   */
61
62 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
63 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
64 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
65
66 /* file identifier strings */
67 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
68 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
69 #define SCORE_COOKIE                    "ROCKSNDIAMONDS_SCORE_FILE_VERSION_1.2"
70
71 /* values for deciding when (not) to save configuration data */
72 #define SAVE_CONF_NEVER                 0
73 #define SAVE_CONF_ALWAYS                1
74 #define SAVE_CONF_WHEN_CHANGED          -1
75
76 /* values for chunks using micro chunks */
77 #define CONF_MASK_1_BYTE                0x00
78 #define CONF_MASK_2_BYTE                0x40
79 #define CONF_MASK_4_BYTE                0x80
80 #define CONF_MASK_MULTI_BYTES           0xc0
81
82 #define CONF_MASK_BYTES                 0xc0
83 #define CONF_MASK_TOKEN                 0x3f
84
85 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
86 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
87 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
88 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
89
90 /* these definitions are just for convenience of use and readability */
91 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
92 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
93 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
94 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
95
96 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
97                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
98                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
99
100 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
101 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
102 #define CONF_ELEMENT_NUM_BYTES          (2)
103
104 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
105                                          (t) == TYPE_ELEMENT_LIST ?     \
106                                          CONF_ELEMENT_NUM_BYTES :       \
107                                          (t) == TYPE_CONTENT ||         \
108                                          (t) == TYPE_CONTENT_LIST ?     \
109                                          CONF_CONTENT_NUM_BYTES : 1)
110
111 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
112 #define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
113                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
114
115 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
116                                          (y) * 3 + (x))
117 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
118                                          CONF_ELEMENT_NUM_BYTES)
119 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
120                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
121
122 /* temporary variables used to store pointers to structure members */
123 static struct LevelInfo li;
124 static struct ElementInfo xx_ei, yy_ei;
125 static struct ElementChangeInfo xx_change;
126 static struct ElementGroupInfo xx_group;
127 static struct EnvelopeInfo xx_envelope;
128 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
129 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
130 static int xx_num_contents;
131 static int xx_current_change_page;
132 static char xx_default_string_empty[1] = "";
133 static int xx_string_length_unused;
134
135 struct LevelFileConfigInfo
136 {
137   int element;                  /* element for which data is to be stored */
138   int save_type;                /* save data always, never or when changed */
139   int data_type;                /* data type (used internally, not stored) */
140   int conf_type;                /* micro chunk identifier (stored in file) */
141
142   /* (mandatory) */
143   void *value;                  /* variable that holds the data to be stored */
144   int default_value;            /* initial default value for this variable */
145
146   /* (optional) */
147   void *value_copy;             /* variable that holds the data to be copied */
148   void *num_entities;           /* number of entities for multi-byte data */
149   int default_num_entities;     /* default number of entities for this data */
150   int max_num_entities;         /* maximal number of entities for this data */
151   char *default_string;         /* optional default string for string data */
152 };
153
154 static struct LevelFileConfigInfo chunk_config_INFO[] =
155 {
156   /* ---------- values not related to single elements ----------------------- */
157
158   {
159     -1,                                 SAVE_CONF_ALWAYS,
160     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
161     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
162   },
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
167     &li.fieldx,                         STD_LEV_FIELDX
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
172     &li.fieldy,                         STD_LEV_FIELDY
173   },
174
175   {
176     -1,                                 SAVE_CONF_ALWAYS,
177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
178     &li.time,                           100
179   },
180
181   {
182     -1,                                 SAVE_CONF_ALWAYS,
183     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
184     &li.gems_needed,                    0
185   },
186
187   {
188     -1,                                 -1,
189     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
190     &li.random_seed,                    0
191   },
192
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204
205   {
206     -1,                                 -1,
207     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
208     &li.em_slippery_gems,               FALSE
209   },
210
211   {
212     -1,                                 -1,
213     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
214     &li.use_custom_template,            FALSE
215   },
216
217   {
218     -1,                                 -1,
219     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
220     &li.can_move_into_acid_bits,        ~0      /* default: everything can */
221   },
222
223   {
224     -1,                                 -1,
225     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
226     &li.dont_collide_with_bits,         ~0      /* default: always deadly */
227   },
228
229   {
230     -1,                                 -1,
231     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
232     &li.em_explodes_by_fire,            FALSE
233   },
234
235   {
236     -1,                                 -1,
237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
238     &li.score[SC_TIME_BONUS],           1
239   },
240
241   {
242     -1,                                 -1,
243     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
244     &li.auto_exit_sokoban,              FALSE
245   },
246
247   {
248     -1,                                 -1,
249     -1,                                 -1,
250     NULL,                               -1
251   }
252 };
253
254 static struct LevelFileConfigInfo chunk_config_ELEM[] =
255 {
256   /* (these values are the same for each player) */
257   {
258     EL_PLAYER_1,                        -1,
259     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
260     &li.block_last_field,               FALSE   /* default case for EM levels */
261   },
262   {
263     EL_PLAYER_1,                        -1,
264     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
265     &li.sp_block_last_field,            TRUE    /* default case for SP levels */
266   },
267   {
268     EL_PLAYER_1,                        -1,
269     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
270     &li.instant_relocation,             FALSE
271   },
272   {
273     EL_PLAYER_1,                        -1,
274     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
275     &li.can_pass_to_walkable,           FALSE
276   },
277   {
278     EL_PLAYER_1,                        -1,
279     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
280     &li.block_snap_field,               TRUE
281   },
282   {
283     EL_PLAYER_1,                        -1,
284     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
285     &li.continuous_snapping,            TRUE
286   },
287   {
288     EL_PLAYER_1,                        -1,
289     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
290     &li.shifted_relocation,             FALSE
291   },
292   {
293     EL_PLAYER_1,                        -1,
294     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
295     &li.lazy_relocation,                FALSE
296   },
297
298   /* (these values are different for each player) */
299   {
300     EL_PLAYER_1,                        -1,
301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
302     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
303   },
304   {
305     EL_PLAYER_1,                        -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
307     &li.initial_player_gravity[0],      FALSE
308   },
309   {
310     EL_PLAYER_1,                        -1,
311     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
312     &li.use_start_element[0],           FALSE
313   },
314   {
315     EL_PLAYER_1,                        -1,
316     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
317     &li.start_element[0],               EL_PLAYER_1
318   },
319   {
320     EL_PLAYER_1,                        -1,
321     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
322     &li.use_artwork_element[0],         FALSE
323   },
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
327     &li.artwork_element[0],             EL_PLAYER_1
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
332     &li.use_explosion_element[0],       FALSE
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
337     &li.explosion_element[0],           EL_PLAYER_1
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
342     &li.use_initial_inventory[0],       FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
347     &li.initial_inventory_size[0],      1
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
352     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
353     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
354   },
355
356   {
357     EL_PLAYER_2,                        -1,
358     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
359     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
360   },
361   {
362     EL_PLAYER_2,                        -1,
363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
364     &li.initial_player_gravity[1],      FALSE
365   },
366   {
367     EL_PLAYER_2,                        -1,
368     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
369     &li.use_start_element[1],           FALSE
370   },
371   {
372     EL_PLAYER_2,                        -1,
373     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
374     &li.start_element[1],               EL_PLAYER_2
375   },
376   {
377     EL_PLAYER_2,                        -1,
378     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
379     &li.use_artwork_element[1],         FALSE
380   },
381   {
382     EL_PLAYER_2,                        -1,
383     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
384     &li.artwork_element[1],             EL_PLAYER_2
385   },
386   {
387     EL_PLAYER_2,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
389     &li.use_explosion_element[1],       FALSE
390   },
391   {
392     EL_PLAYER_2,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
394     &li.explosion_element[1],           EL_PLAYER_2
395   },
396   {
397     EL_PLAYER_2,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
399     &li.use_initial_inventory[1],       FALSE
400   },
401   {
402     EL_PLAYER_2,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
404     &li.initial_inventory_size[1],      1
405   },
406   {
407     EL_PLAYER_2,                        -1,
408     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
409     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
410     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
411   },
412
413   {
414     EL_PLAYER_3,                        -1,
415     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
416     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
417   },
418   {
419     EL_PLAYER_3,                        -1,
420     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
421     &li.initial_player_gravity[2],      FALSE
422   },
423   {
424     EL_PLAYER_3,                        -1,
425     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
426     &li.use_start_element[2],           FALSE
427   },
428   {
429     EL_PLAYER_3,                        -1,
430     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
431     &li.start_element[2],               EL_PLAYER_3
432   },
433   {
434     EL_PLAYER_3,                        -1,
435     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
436     &li.use_artwork_element[2],         FALSE
437   },
438   {
439     EL_PLAYER_3,                        -1,
440     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
441     &li.artwork_element[2],             EL_PLAYER_3
442   },
443   {
444     EL_PLAYER_3,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
446     &li.use_explosion_element[2],       FALSE
447   },
448   {
449     EL_PLAYER_3,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
451     &li.explosion_element[2],           EL_PLAYER_3
452   },
453   {
454     EL_PLAYER_3,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
456     &li.use_initial_inventory[2],       FALSE
457   },
458   {
459     EL_PLAYER_3,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
461     &li.initial_inventory_size[2],      1
462   },
463   {
464     EL_PLAYER_3,                        -1,
465     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
466     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
467     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
468   },
469
470   {
471     EL_PLAYER_4,                        -1,
472     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
473     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
474   },
475   {
476     EL_PLAYER_4,                        -1,
477     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
478     &li.initial_player_gravity[3],      FALSE
479   },
480   {
481     EL_PLAYER_4,                        -1,
482     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
483     &li.use_start_element[3],           FALSE
484   },
485   {
486     EL_PLAYER_4,                        -1,
487     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
488     &li.start_element[3],               EL_PLAYER_4
489   },
490   {
491     EL_PLAYER_4,                        -1,
492     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
493     &li.use_artwork_element[3],         FALSE
494   },
495   {
496     EL_PLAYER_4,                        -1,
497     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
498     &li.artwork_element[3],             EL_PLAYER_4
499   },
500   {
501     EL_PLAYER_4,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
503     &li.use_explosion_element[3],       FALSE
504   },
505   {
506     EL_PLAYER_4,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
508     &li.explosion_element[3],           EL_PLAYER_4
509   },
510   {
511     EL_PLAYER_4,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
513     &li.use_initial_inventory[3],       FALSE
514   },
515   {
516     EL_PLAYER_4,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
518     &li.initial_inventory_size[3],      1
519   },
520   {
521     EL_PLAYER_4,                        -1,
522     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
523     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
524     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
525   },
526
527   {
528     EL_EMERALD,                         -1,
529     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
530     &li.score[SC_EMERALD],              10
531   },
532
533   {
534     EL_DIAMOND,                         -1,
535     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
536     &li.score[SC_DIAMOND],              10
537   },
538
539   {
540     EL_BUG,                             -1,
541     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
542     &li.score[SC_BUG],                  10
543   },
544
545   {
546     EL_SPACESHIP,                       -1,
547     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
548     &li.score[SC_SPACESHIP],            10
549   },
550
551   {
552     EL_PACMAN,                          -1,
553     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
554     &li.score[SC_PACMAN],               10
555   },
556
557   {
558     EL_NUT,                             -1,
559     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
560     &li.score[SC_NUT],                  10
561   },
562
563   {
564     EL_DYNAMITE,                        -1,
565     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
566     &li.score[SC_DYNAMITE],             10
567   },
568
569   {
570     EL_KEY_1,                           -1,
571     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
572     &li.score[SC_KEY],                  10
573   },
574
575   {
576     EL_PEARL,                           -1,
577     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
578     &li.score[SC_PEARL],                10
579   },
580
581   {
582     EL_CRYSTAL,                         -1,
583     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
584     &li.score[SC_CRYSTAL],              10
585   },
586
587   {
588     EL_BD_AMOEBA,                       -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
590     &li.amoeba_content,                 EL_DIAMOND
591   },
592   {
593     EL_BD_AMOEBA,                       -1,
594     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
595     &li.amoeba_speed,                   10
596   },
597   {
598     EL_BD_AMOEBA,                       -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
600     &li.grow_into_diggable,             TRUE
601   },
602
603   {
604     EL_YAMYAM,                          -1,
605     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
606     &li.yamyam_content,                 EL_ROCK, NULL,
607     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
608   },
609   {
610     EL_YAMYAM,                          -1,
611     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
612     &li.score[SC_YAMYAM],               10
613   },
614
615   {
616     EL_ROBOT,                           -1,
617     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
618     &li.score[SC_ROBOT],                10
619   },
620   {
621     EL_ROBOT,                           -1,
622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
623     &li.slurp_score,                    10
624   },
625
626   {
627     EL_ROBOT_WHEEL,                     -1,
628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
629     &li.time_wheel,                     10
630   },
631
632   {
633     EL_MAGIC_WALL,                      -1,
634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
635     &li.time_magic_wall,                10
636   },
637
638   {
639     EL_GAME_OF_LIFE,                    -1,
640     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
641     &li.game_of_life[0],                2
642   },
643   {
644     EL_GAME_OF_LIFE,                    -1,
645     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
646     &li.game_of_life[1],                3
647   },
648   {
649     EL_GAME_OF_LIFE,                    -1,
650     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
651     &li.game_of_life[2],                3
652   },
653   {
654     EL_GAME_OF_LIFE,                    -1,
655     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
656     &li.game_of_life[3],                3
657   },
658
659   {
660     EL_BIOMAZE,                         -1,
661     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
662     &li.biomaze[0],                     2
663   },
664   {
665     EL_BIOMAZE,                         -1,
666     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
667     &li.biomaze[1],                     3
668   },
669   {
670     EL_BIOMAZE,                         -1,
671     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
672     &li.biomaze[2],                     3
673   },
674   {
675     EL_BIOMAZE,                         -1,
676     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
677     &li.biomaze[3],                     3
678   },
679
680   {
681     EL_TIMEGATE_SWITCH,                 -1,
682     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
683     &li.time_timegate,                  10
684   },
685
686   {
687     EL_LIGHT_SWITCH_ACTIVE,             -1,
688     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
689     &li.time_light,                     10
690   },
691
692   {
693     EL_SHIELD_NORMAL,                   -1,
694     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
695     &li.shield_normal_time,             10
696   },
697   {
698     EL_SHIELD_NORMAL,                   -1,
699     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
700     &li.score[SC_SHIELD],               10
701   },
702
703   {
704     EL_SHIELD_DEADLY,                   -1,
705     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
706     &li.shield_deadly_time,             10
707   },
708   {
709     EL_SHIELD_DEADLY,                   -1,
710     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
711     &li.score[SC_SHIELD],               10
712   },
713
714   {
715     EL_EXTRA_TIME,                      -1,
716     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
717     &li.extra_time,                     10
718   },
719   {
720     EL_EXTRA_TIME,                      -1,
721     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
722     &li.extra_time_score,               10
723   },
724
725   {
726     EL_TIME_ORB_FULL,                   -1,
727     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
728     &li.time_orb_time,                  10
729   },
730   {
731     EL_TIME_ORB_FULL,                   -1,
732     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
733     &li.use_time_orb_bug,               FALSE
734   },
735
736   {
737     EL_SPRING,                          -1,
738     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
739     &li.use_spring_bug,                 FALSE
740   },
741
742   {
743     EL_EMC_ANDROID,                     -1,
744     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
745     &li.android_move_time,              10
746   },
747   {
748     EL_EMC_ANDROID,                     -1,
749     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
750     &li.android_clone_time,             10
751   },
752   {
753     EL_EMC_ANDROID,                     -1,
754     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
755     &li.android_clone_element[0],       EL_EMPTY, NULL,
756     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
757   },
758
759   {
760     EL_EMC_LENSES,                      -1,
761     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
762     &li.lenses_score,                   10
763   },
764   {
765     EL_EMC_LENSES,                      -1,
766     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
767     &li.lenses_time,                    10
768   },
769
770   {
771     EL_EMC_MAGNIFIER,                   -1,
772     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
773     &li.magnify_score,                  10
774   },
775   {
776     EL_EMC_MAGNIFIER,                   -1,
777     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
778     &li.magnify_time,                   10
779   },
780
781   {
782     EL_EMC_MAGIC_BALL,                  -1,
783     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
784     &li.ball_time,                      10
785   },
786   {
787     EL_EMC_MAGIC_BALL,                  -1,
788     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
789     &li.ball_random,                    FALSE
790   },
791   {
792     EL_EMC_MAGIC_BALL,                  -1,
793     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
794     &li.ball_state_initial,             FALSE
795   },
796   {
797     EL_EMC_MAGIC_BALL,                  -1,
798     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
799     &li.ball_content,                   EL_EMPTY, NULL,
800     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
801   },
802
803   /* ---------- unused values ----------------------------------------------- */
804
805   {
806     EL_UNKNOWN,                         SAVE_CONF_NEVER,
807     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
808     &li.score[SC_UNKNOWN_14],           10
809   },
810   {
811     EL_UNKNOWN,                         SAVE_CONF_NEVER,
812     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
813     &li.score[SC_UNKNOWN_15],           10
814   },
815
816   {
817     -1,                                 -1,
818     -1,                                 -1,
819     NULL,                               -1
820   }
821 };
822
823 static struct LevelFileConfigInfo chunk_config_NOTE[] =
824 {
825   {
826     -1,                                 -1,
827     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
828     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
829   },
830   {
831     -1,                                 -1,
832     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
833     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
834   },
835
836   {
837     -1,                                 -1,
838     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
839     &xx_envelope.autowrap,              FALSE
840   },
841   {
842     -1,                                 -1,
843     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
844     &xx_envelope.centered,              FALSE
845   },
846
847   {
848     -1,                                 -1,
849     TYPE_STRING,                        CONF_VALUE_BYTES(1),
850     &xx_envelope.text,                  -1, NULL,
851     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
852     &xx_default_string_empty[0]
853   },
854
855   {
856     -1,                                 -1,
857     -1,                                 -1,
858     NULL,                               -1
859   }
860 };
861
862 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
863 {
864   {
865     -1,                                 -1,
866     TYPE_STRING,                        CONF_VALUE_BYTES(1),
867     &xx_ei.description[0],              -1,
868     &yy_ei.description[0],
869     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
870     &xx_default_description[0]
871   },
872
873   {
874     -1,                                 -1,
875     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
876     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
877     &yy_ei.properties[EP_BITFIELD_BASE_NR]
878   },
879 #if ENABLE_RESERVED_CODE
880   /* (reserved for later use) */
881   {
882     -1,                                 -1,
883     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
884     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
885     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
886   },
887 #endif
888
889   {
890     -1,                                 -1,
891     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
892     &xx_ei.use_gfx_element,             FALSE,
893     &yy_ei.use_gfx_element
894   },
895   {
896     -1,                                 -1,
897     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
898     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
899     &yy_ei.gfx_element_initial
900   },
901
902   {
903     -1,                                 -1,
904     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
905     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
906     &yy_ei.access_direction
907   },
908
909   {
910     -1,                                 -1,
911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
912     &xx_ei.collect_score_initial,       10,
913     &yy_ei.collect_score_initial
914   },
915   {
916     -1,                                 -1,
917     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
918     &xx_ei.collect_count_initial,       1,
919     &yy_ei.collect_count_initial
920   },
921
922   {
923     -1,                                 -1,
924     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
925     &xx_ei.ce_value_fixed_initial,      0,
926     &yy_ei.ce_value_fixed_initial
927   },
928   {
929     -1,                                 -1,
930     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
931     &xx_ei.ce_value_random_initial,     0,
932     &yy_ei.ce_value_random_initial
933   },
934   {
935     -1,                                 -1,
936     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
937     &xx_ei.use_last_ce_value,           FALSE,
938     &yy_ei.use_last_ce_value
939   },
940
941   {
942     -1,                                 -1,
943     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
944     &xx_ei.push_delay_fixed,            8,
945     &yy_ei.push_delay_fixed
946   },
947   {
948     -1,                                 -1,
949     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
950     &xx_ei.push_delay_random,           8,
951     &yy_ei.push_delay_random
952   },
953   {
954     -1,                                 -1,
955     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
956     &xx_ei.drop_delay_fixed,            0,
957     &yy_ei.drop_delay_fixed
958   },
959   {
960     -1,                                 -1,
961     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
962     &xx_ei.drop_delay_random,           0,
963     &yy_ei.drop_delay_random
964   },
965   {
966     -1,                                 -1,
967     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
968     &xx_ei.move_delay_fixed,            0,
969     &yy_ei.move_delay_fixed
970   },
971   {
972     -1,                                 -1,
973     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
974     &xx_ei.move_delay_random,           0,
975     &yy_ei.move_delay_random
976   },
977
978   {
979     -1,                                 -1,
980     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
981     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
982     &yy_ei.move_pattern
983   },
984   {
985     -1,                                 -1,
986     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
987     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
988     &yy_ei.move_direction_initial
989   },
990   {
991     -1,                                 -1,
992     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
993     &xx_ei.move_stepsize,               TILEX / 8,
994     &yy_ei.move_stepsize
995   },
996
997   {
998     -1,                                 -1,
999     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1000     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1001     &yy_ei.move_enter_element
1002   },
1003   {
1004     -1,                                 -1,
1005     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1006     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1007     &yy_ei.move_leave_element
1008   },
1009   {
1010     -1,                                 -1,
1011     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1012     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1013     &yy_ei.move_leave_type
1014   },
1015
1016   {
1017     -1,                                 -1,
1018     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1019     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1020     &yy_ei.slippery_type
1021   },
1022
1023   {
1024     -1,                                 -1,
1025     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1026     &xx_ei.explosion_type,              EXPLODES_3X3,
1027     &yy_ei.explosion_type
1028   },
1029   {
1030     -1,                                 -1,
1031     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1032     &xx_ei.explosion_delay,             16,
1033     &yy_ei.explosion_delay
1034   },
1035   {
1036     -1,                                 -1,
1037     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1038     &xx_ei.ignition_delay,              8,
1039     &yy_ei.ignition_delay
1040   },
1041
1042   {
1043     -1,                                 -1,
1044     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1045     &xx_ei.content,                     EL_EMPTY_SPACE,
1046     &yy_ei.content,
1047     &xx_num_contents,                   1, 1
1048   },
1049
1050   /* ---------- "num_change_pages" must be the last entry ------------------- */
1051
1052   {
1053     -1,                                 SAVE_CONF_ALWAYS,
1054     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1055     &xx_ei.num_change_pages,            1,
1056     &yy_ei.num_change_pages
1057   },
1058
1059   {
1060     -1,                                 -1,
1061     -1,                                 -1,
1062     NULL,                               -1,
1063     NULL
1064   }
1065 };
1066
1067 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1068 {
1069   /* ---------- "current_change_page" must be the first entry --------------- */
1070
1071   {
1072     -1,                                 SAVE_CONF_ALWAYS,
1073     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1074     &xx_current_change_page,            -1
1075   },
1076
1077   /* ---------- (the remaining entries can be in any order) ----------------- */
1078
1079   {
1080     -1,                                 -1,
1081     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1082     &xx_change.can_change,              FALSE
1083   },
1084
1085   {
1086     -1,                                 -1,
1087     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1088     &xx_event_bits[0],                  0
1089   },
1090   {
1091     -1,                                 -1,
1092     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1093     &xx_event_bits[1],                  0
1094   },
1095
1096   {
1097     -1,                                 -1,
1098     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1099     &xx_change.trigger_player,          CH_PLAYER_ANY
1100   },
1101   {
1102     -1,                                 -1,
1103     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1104     &xx_change.trigger_side,            CH_SIDE_ANY
1105   },
1106   {
1107     -1,                                 -1,
1108     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1109     &xx_change.trigger_page,            CH_PAGE_ANY
1110   },
1111
1112   {
1113     -1,                                 -1,
1114     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1115     &xx_change.target_element,          EL_EMPTY_SPACE
1116   },
1117
1118   {
1119     -1,                                 -1,
1120     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1121     &xx_change.delay_fixed,             0
1122   },
1123   {
1124     -1,                                 -1,
1125     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1126     &xx_change.delay_random,            0
1127   },
1128   {
1129     -1,                                 -1,
1130     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1131     &xx_change.delay_frames,            FRAMES_PER_SECOND
1132   },
1133
1134   {
1135     -1,                                 -1,
1136     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1137     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1138   },
1139
1140   {
1141     -1,                                 -1,
1142     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1143     &xx_change.explode,                 FALSE
1144   },
1145   {
1146     -1,                                 -1,
1147     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1148     &xx_change.use_target_content,      FALSE
1149   },
1150   {
1151     -1,                                 -1,
1152     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1153     &xx_change.only_if_complete,        FALSE
1154   },
1155   {
1156     -1,                                 -1,
1157     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1158     &xx_change.use_random_replace,      FALSE
1159   },
1160   {
1161     -1,                                 -1,
1162     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1163     &xx_change.random_percentage,       100
1164   },
1165   {
1166     -1,                                 -1,
1167     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1168     &xx_change.replace_when,            CP_WHEN_EMPTY
1169   },
1170
1171   {
1172     -1,                                 -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1174     &xx_change.has_action,              FALSE
1175   },
1176   {
1177     -1,                                 -1,
1178     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1179     &xx_change.action_type,             CA_NO_ACTION
1180   },
1181   {
1182     -1,                                 -1,
1183     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1184     &xx_change.action_mode,             CA_MODE_UNDEFINED
1185   },
1186   {
1187     -1,                                 -1,
1188     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1189     &xx_change.action_arg,              CA_ARG_UNDEFINED
1190   },
1191
1192   {
1193     -1,                                 -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1195     &xx_change.action_element,          EL_EMPTY_SPACE
1196   },
1197
1198   {
1199     -1,                                 -1,
1200     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1201     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1202     &xx_num_contents,                   1, 1
1203   },
1204
1205   {
1206     -1,                                 -1,
1207     -1,                                 -1,
1208     NULL,                               -1
1209   }
1210 };
1211
1212 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1213 {
1214   {
1215     -1,                                 -1,
1216     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1217     &xx_ei.description[0],              -1, NULL,
1218     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1219     &xx_default_description[0]
1220   },
1221
1222   {
1223     -1,                                 -1,
1224     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1225     &xx_ei.use_gfx_element,             FALSE
1226   },
1227   {
1228     -1,                                 -1,
1229     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1230     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1231   },
1232
1233   {
1234     -1,                                 -1,
1235     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1236     &xx_group.choice_mode,              ANIM_RANDOM
1237   },
1238
1239   {
1240     -1,                                 -1,
1241     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1242     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1243     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1244   },
1245
1246   {
1247     -1,                                 -1,
1248     -1,                                 -1,
1249     NULL,                               -1
1250   }
1251 };
1252
1253 static struct LevelFileConfigInfo chunk_config_CONF[] =         /* (OBSOLETE) */
1254 {
1255   {
1256     EL_PLAYER_1,                        -1,
1257     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1258     &li.block_snap_field,               TRUE
1259   },
1260   {
1261     EL_PLAYER_1,                        -1,
1262     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1263     &li.continuous_snapping,            TRUE
1264   },
1265   {
1266     EL_PLAYER_1,                        -1,
1267     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1268     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1269   },
1270   {
1271     EL_PLAYER_1,                        -1,
1272     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1273     &li.use_start_element[0],           FALSE
1274   },
1275   {
1276     EL_PLAYER_1,                        -1,
1277     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1278     &li.start_element[0],               EL_PLAYER_1
1279   },
1280   {
1281     EL_PLAYER_1,                        -1,
1282     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1283     &li.use_artwork_element[0],         FALSE
1284   },
1285   {
1286     EL_PLAYER_1,                        -1,
1287     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1288     &li.artwork_element[0],             EL_PLAYER_1
1289   },
1290   {
1291     EL_PLAYER_1,                        -1,
1292     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1293     &li.use_explosion_element[0],       FALSE
1294   },
1295   {
1296     EL_PLAYER_1,                        -1,
1297     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1298     &li.explosion_element[0],           EL_PLAYER_1
1299   },
1300
1301   {
1302     -1,                                 -1,
1303     -1,                                 -1,
1304     NULL,                               -1
1305   }
1306 };
1307
1308 static struct
1309 {
1310   int filetype;
1311   char *id;
1312 }
1313 filetype_id_list[] =
1314 {
1315   { LEVEL_FILE_TYPE_RND,        "RND"   },
1316   { LEVEL_FILE_TYPE_BD,         "BD"    },
1317   { LEVEL_FILE_TYPE_EM,         "EM"    },
1318   { LEVEL_FILE_TYPE_SP,         "SP"    },
1319   { LEVEL_FILE_TYPE_DX,         "DX"    },
1320   { LEVEL_FILE_TYPE_SB,         "SB"    },
1321   { LEVEL_FILE_TYPE_DC,         "DC"    },
1322   { -1,                         NULL    },
1323 };
1324
1325
1326 /* ========================================================================= */
1327 /* level file functions                                                      */
1328 /* ========================================================================= */
1329
1330 static boolean check_special_flags(char *flag)
1331 {
1332   if (strEqual(options.special_flags, flag) ||
1333       strEqual(leveldir_current->special_flags, flag))
1334     return TRUE;
1335
1336   return FALSE;
1337 }
1338
1339 static struct DateInfo getCurrentDate()
1340 {
1341   time_t epoch_seconds = time(NULL);
1342   struct tm *now = localtime(&epoch_seconds);
1343   struct DateInfo date;
1344
1345   date.year  = now->tm_year + 1900;
1346   date.month = now->tm_mon  + 1;
1347   date.day   = now->tm_mday;
1348
1349   date.src   = DATE_SRC_CLOCK;
1350
1351   return date;
1352 }
1353
1354 static void resetEventFlags(struct ElementChangeInfo *change)
1355 {
1356   int i;
1357
1358   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1359     change->has_event[i] = FALSE;
1360 }
1361
1362 static void resetEventBits()
1363 {
1364   int i;
1365
1366   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1367     xx_event_bits[i] = 0;
1368 }
1369
1370 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1371 {
1372   int i;
1373
1374   /* important: only change event flag if corresponding event bit is set
1375      (this is because all xx_event_bits[] values are loaded separately,
1376      and all xx_event_bits[] values are set back to zero before loading
1377      another value xx_event_bits[x] (each value representing 32 flags)) */
1378
1379   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1380     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1381       change->has_event[i] = TRUE;
1382 }
1383
1384 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1385 {
1386   int i;
1387
1388   /* in contrast to the above function setEventFlagsFromEventBits(), it
1389      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1390      depending on the corresponding change->has_event[i] values here, as
1391      all xx_event_bits[] values are reset in resetEventBits() before */
1392
1393   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1394     if (change->has_event[i])
1395       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1396 }
1397
1398 static char *getDefaultElementDescription(struct ElementInfo *ei)
1399 {
1400   static char description[MAX_ELEMENT_NAME_LEN + 1];
1401   char *default_description = (ei->custom_description != NULL ?
1402                                ei->custom_description :
1403                                ei->editor_description);
1404   int i;
1405
1406   /* always start with reliable default values */
1407   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1408     description[i] = '\0';
1409
1410   /* truncate element description to MAX_ELEMENT_NAME_LEN bytes */
1411   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1412
1413   return &description[0];
1414 }
1415
1416 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1417 {
1418   char *default_description = getDefaultElementDescription(ei);
1419   int i;
1420
1421   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1422     ei->description[i] = default_description[i];
1423 }
1424
1425 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1426 {
1427   int i;
1428
1429   for (i = 0; conf[i].data_type != -1; i++)
1430   {
1431     int default_value = conf[i].default_value;
1432     int data_type = conf[i].data_type;
1433     int conf_type = conf[i].conf_type;
1434     int byte_mask = conf_type & CONF_MASK_BYTES;
1435
1436     if (byte_mask == CONF_MASK_MULTI_BYTES)
1437     {
1438       int default_num_entities = conf[i].default_num_entities;
1439       int max_num_entities = conf[i].max_num_entities;
1440
1441       *(int *)(conf[i].num_entities) = default_num_entities;
1442
1443       if (data_type == TYPE_STRING)
1444       {
1445         char *default_string = conf[i].default_string;
1446         char *string = (char *)(conf[i].value);
1447
1448         strncpy(string, default_string, max_num_entities);
1449       }
1450       else if (data_type == TYPE_ELEMENT_LIST)
1451       {
1452         int *element_array = (int *)(conf[i].value);
1453         int j;
1454
1455         for (j = 0; j < max_num_entities; j++)
1456           element_array[j] = default_value;
1457       }
1458       else if (data_type == TYPE_CONTENT_LIST)
1459       {
1460         struct Content *content = (struct Content *)(conf[i].value);
1461         int c, x, y;
1462
1463         for (c = 0; c < max_num_entities; c++)
1464           for (y = 0; y < 3; y++)
1465             for (x = 0; x < 3; x++)
1466               content[c].e[x][y] = default_value;
1467       }
1468     }
1469     else        /* constant size configuration data (1, 2 or 4 bytes) */
1470     {
1471       if (data_type == TYPE_BOOLEAN)
1472         *(boolean *)(conf[i].value) = default_value;
1473       else
1474         *(int *)    (conf[i].value) = default_value;
1475     }
1476   }
1477 }
1478
1479 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
1480 {
1481   int i;
1482
1483   for (i = 0; conf[i].data_type != -1; i++)
1484   {
1485     int data_type = conf[i].data_type;
1486     int conf_type = conf[i].conf_type;
1487     int byte_mask = conf_type & CONF_MASK_BYTES;
1488
1489     if (byte_mask == CONF_MASK_MULTI_BYTES)
1490     {
1491       int max_num_entities = conf[i].max_num_entities;
1492
1493       if (data_type == TYPE_STRING)
1494       {
1495         char *string      = (char *)(conf[i].value);
1496         char *string_copy = (char *)(conf[i].value_copy);
1497
1498         strncpy(string_copy, string, max_num_entities);
1499       }
1500       else if (data_type == TYPE_ELEMENT_LIST)
1501       {
1502         int *element_array      = (int *)(conf[i].value);
1503         int *element_array_copy = (int *)(conf[i].value_copy);
1504         int j;
1505
1506         for (j = 0; j < max_num_entities; j++)
1507           element_array_copy[j] = element_array[j];
1508       }
1509       else if (data_type == TYPE_CONTENT_LIST)
1510       {
1511         struct Content *content      = (struct Content *)(conf[i].value);
1512         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
1513         int c, x, y;
1514
1515         for (c = 0; c < max_num_entities; c++)
1516           for (y = 0; y < 3; y++)
1517             for (x = 0; x < 3; x++)
1518               content_copy[c].e[x][y] = content[c].e[x][y];
1519       }
1520     }
1521     else        /* constant size configuration data (1, 2 or 4 bytes) */
1522     {
1523       if (data_type == TYPE_BOOLEAN)
1524         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
1525       else
1526         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
1527     }
1528   }
1529 }
1530
1531 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
1532 {
1533   int i;
1534
1535   xx_ei = *ei_from;     /* copy element data into temporary buffer */
1536   yy_ei = *ei_to;       /* copy element data into temporary buffer */
1537
1538   copyConfigFromConfigList(chunk_config_CUSX_base);
1539
1540   *ei_from = xx_ei;
1541   *ei_to   = yy_ei;
1542
1543   /* ---------- reinitialize and copy change pages ---------- */
1544
1545   ei_to->num_change_pages = ei_from->num_change_pages;
1546   ei_to->current_change_page = ei_from->current_change_page;
1547
1548   setElementChangePages(ei_to, ei_to->num_change_pages);
1549
1550   for (i = 0; i < ei_to->num_change_pages; i++)
1551     ei_to->change_page[i] = ei_from->change_page[i];
1552
1553   /* ---------- copy group element info ---------- */
1554   if (ei_from->group != NULL && ei_to->group != NULL)   /* group or internal */
1555     *ei_to->group = *ei_from->group;
1556
1557   /* mark this custom element as modified */
1558   ei_to->modified_settings = TRUE;
1559 }
1560
1561 void setElementChangePages(struct ElementInfo *ei, int change_pages)
1562 {
1563   int change_page_size = sizeof(struct ElementChangeInfo);
1564
1565   ei->num_change_pages = MAX(1, change_pages);
1566
1567   ei->change_page =
1568     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
1569
1570   if (ei->current_change_page >= ei->num_change_pages)
1571     ei->current_change_page = ei->num_change_pages - 1;
1572
1573   ei->change = &ei->change_page[ei->current_change_page];
1574 }
1575
1576 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
1577 {
1578   xx_change = *change;          /* copy change data into temporary buffer */
1579
1580   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
1581
1582   *change = xx_change;
1583
1584   resetEventFlags(change);
1585
1586   change->direct_action = 0;
1587   change->other_action = 0;
1588
1589   change->pre_change_function = NULL;
1590   change->change_function = NULL;
1591   change->post_change_function = NULL;
1592 }
1593
1594 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
1595 {
1596   int i, x, y;
1597
1598   li = *level;          /* copy level data into temporary buffer */
1599   setConfigToDefaultsFromConfigList(chunk_config_INFO);
1600   *level = li;          /* copy temporary buffer back to level data */
1601
1602   setLevelInfoToDefaults_EM();
1603   setLevelInfoToDefaults_SP();
1604
1605   level->native_em_level = &native_em_level;
1606   level->native_sp_level = &native_sp_level;
1607
1608   level->file_version = FILE_VERSION_ACTUAL;
1609   level->game_version = GAME_VERSION_ACTUAL;
1610
1611   level->creation_date = getCurrentDate();
1612
1613   level->encoding_16bit_field  = TRUE;
1614   level->encoding_16bit_yamyam = TRUE;
1615   level->encoding_16bit_amoeba = TRUE;
1616
1617   /* clear level name and level author string buffers */
1618   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
1619     level->name[i] = '\0';
1620   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
1621     level->author[i] = '\0';
1622
1623   /* set level name and level author to default values */
1624   strcpy(level->name, NAMELESS_LEVEL_NAME);
1625   strcpy(level->author, ANONYMOUS_NAME);
1626
1627   /* set level playfield to playable default level with player and exit */
1628   for (x = 0; x < MAX_LEV_FIELDX; x++)
1629     for (y = 0; y < MAX_LEV_FIELDY; y++)
1630       level->field[x][y] = EL_SAND;
1631
1632   level->field[0][0] = EL_PLAYER_1;
1633   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
1634
1635   BorderElement = EL_STEELWALL;
1636
1637   /* set all bug compatibility flags to "false" => do not emulate this bug */
1638   level->use_action_after_change_bug = FALSE;
1639
1640   if (leveldir_current)
1641   {
1642     /* try to determine better author name than 'anonymous' */
1643     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
1644     {
1645       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
1646       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1647     }
1648     else
1649     {
1650       switch (LEVELCLASS(leveldir_current))
1651       {
1652         case LEVELCLASS_TUTORIAL:
1653           strcpy(level->author, PROGRAM_AUTHOR_STRING);
1654           break;
1655
1656         case LEVELCLASS_CONTRIB:
1657           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
1658           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1659           break;
1660
1661         case LEVELCLASS_PRIVATE:
1662           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
1663           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
1664           break;
1665
1666         default:
1667           /* keep default value */
1668           break;
1669       }
1670     }
1671   }
1672 }
1673
1674 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
1675 {
1676   static boolean clipboard_elements_initialized = FALSE;
1677   int i;
1678
1679   InitElementPropertiesStatic();
1680
1681   li = *level;          /* copy level data into temporary buffer */
1682   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
1683   *level = li;          /* copy temporary buffer back to level data */
1684
1685   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
1686   {
1687     int element = i;
1688     struct ElementInfo *ei = &element_info[element];
1689
1690     /* never initialize clipboard elements after the very first time */
1691     /* (to be able to use clipboard elements between several levels) */
1692     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
1693       continue;
1694
1695     if (IS_ENVELOPE(element))
1696     {
1697       int envelope_nr = element - EL_ENVELOPE_1;
1698
1699       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
1700
1701       level->envelope[envelope_nr] = xx_envelope;
1702     }
1703
1704     if (IS_CUSTOM_ELEMENT(element) ||
1705         IS_GROUP_ELEMENT(element) ||
1706         IS_INTERNAL_ELEMENT(element))
1707     {
1708       xx_ei = *ei;      /* copy element data into temporary buffer */
1709
1710       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
1711
1712       *ei = xx_ei;
1713     }
1714
1715     setElementChangePages(ei, 1);
1716     setElementChangeInfoToDefaults(ei->change);
1717
1718     if (IS_CUSTOM_ELEMENT(element) ||
1719         IS_GROUP_ELEMENT(element) ||
1720         IS_INTERNAL_ELEMENT(element))
1721     {
1722       setElementDescriptionToDefault(ei);
1723
1724       ei->modified_settings = FALSE;
1725     }
1726
1727     if (IS_CUSTOM_ELEMENT(element) ||
1728         IS_INTERNAL_ELEMENT(element))
1729     {
1730       /* internal values used in level editor */
1731
1732       ei->access_type = 0;
1733       ei->access_layer = 0;
1734       ei->access_protected = 0;
1735       ei->walk_to_action = 0;
1736       ei->smash_targets = 0;
1737       ei->deadliness = 0;
1738
1739       ei->can_explode_by_fire = FALSE;
1740       ei->can_explode_smashed = FALSE;
1741       ei->can_explode_impact = FALSE;
1742
1743       ei->current_change_page = 0;
1744     }
1745
1746     if (IS_GROUP_ELEMENT(element) ||
1747         IS_INTERNAL_ELEMENT(element))
1748     {
1749       struct ElementGroupInfo *group;
1750
1751       /* initialize memory for list of elements in group */
1752       if (ei->group == NULL)
1753         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
1754
1755       group = ei->group;
1756
1757       xx_group = *group;        /* copy group data into temporary buffer */
1758
1759       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
1760
1761       *group = xx_group;
1762     }
1763   }
1764
1765   clipboard_elements_initialized = TRUE;
1766 }
1767
1768 static void setLevelInfoToDefaults(struct LevelInfo *level,
1769                                    boolean level_info_only,
1770                                    boolean reset_file_status)
1771 {
1772   setLevelInfoToDefaults_Level(level);
1773
1774   if (!level_info_only)
1775     setLevelInfoToDefaults_Elements(level);
1776
1777   if (reset_file_status)
1778   {
1779     level->no_valid_file = FALSE;
1780     level->no_level_file = FALSE;
1781   }
1782
1783   level->changed = FALSE;
1784 }
1785
1786 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
1787 {
1788   level_file_info->nr = 0;
1789   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
1790   level_file_info->packed = FALSE;
1791   level_file_info->basename = NULL;
1792   level_file_info->filename = NULL;
1793 }
1794
1795 int getMappedElement_SB(int, boolean);
1796
1797 static void ActivateLevelTemplate()
1798 {
1799   int x, y;
1800
1801   if (check_special_flags("load_xsb_to_ces"))
1802   {
1803     /* fill smaller playfields with padding "beyond border wall" elements */
1804     if (level.fieldx < level_template.fieldx ||
1805         level.fieldy < level_template.fieldy)
1806     {
1807       short field[level.fieldx][level.fieldy];
1808       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
1809       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
1810       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
1811       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
1812
1813       /* copy old playfield (which is smaller than the visible area) */
1814       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1815         field[x][y] = level.field[x][y];
1816
1817       /* fill new, larger playfield with "beyond border wall" elements */
1818       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
1819         level.field[x][y] = getMappedElement_SB('_', TRUE);
1820
1821       /* copy the old playfield to the middle of the new playfield */
1822       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
1823         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
1824
1825       level.fieldx = new_fieldx;
1826       level.fieldy = new_fieldy;
1827     }
1828   }
1829
1830   /* Currently there is no special action needed to activate the template
1831      data, because 'element_info' property settings overwrite the original
1832      level data, while all other variables do not change. */
1833
1834   /* Exception: 'from_level_template' elements in the original level playfield
1835      are overwritten with the corresponding elements at the same position in
1836      playfield from the level template. */
1837
1838   for (x = 0; x < level.fieldx; x++)
1839     for (y = 0; y < level.fieldy; y++)
1840       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
1841         level.field[x][y] = level_template.field[x][y];
1842
1843   if (check_special_flags("load_xsb_to_ces"))
1844   {
1845     struct LevelInfo level_backup = level;
1846
1847     /* overwrite all individual level settings from template level settings */
1848     level = level_template;
1849
1850     /* restore playfield size */
1851     level.fieldx = level_backup.fieldx;
1852     level.fieldy = level_backup.fieldy;
1853
1854     /* restore playfield content */
1855     for (x = 0; x < level.fieldx; x++)
1856       for (y = 0; y < level.fieldy; y++)
1857         level.field[x][y] = level_backup.field[x][y];
1858
1859     /* restore name and author from individual level */
1860     strcpy(level.name,   level_backup.name);
1861     strcpy(level.author, level_backup.author);
1862
1863     /* restore flag "use_custom_template" */
1864     level.use_custom_template = level_backup.use_custom_template;
1865   }
1866 }
1867
1868 static char *getLevelFilenameFromBasename(char *basename)
1869 {
1870   static char *filename[2] = { NULL, NULL };
1871   int pos = (strEqual(basename, LEVELTEMPLATE_FILENAME) ? 0 : 1);
1872
1873   checked_free(filename[pos]);
1874
1875   filename[pos] = getPath2(getCurrentLevelDir(), basename);
1876
1877   return filename[pos];
1878 }
1879
1880 static int getFileTypeFromBasename(char *basename)
1881 {
1882   /* !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!! */
1883
1884   static char *filename = NULL;
1885   struct stat file_status;
1886
1887   /* ---------- try to determine file type from filename ---------- */
1888
1889   /* check for typical filename of a Supaplex level package file */
1890   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
1891     return LEVEL_FILE_TYPE_SP;
1892
1893   /* check for typical filename of a Diamond Caves II level package file */
1894   if (strSuffixLower(basename, ".dc") ||
1895       strSuffixLower(basename, ".dc2"))
1896     return LEVEL_FILE_TYPE_DC;
1897
1898   /* check for typical filename of a Sokoban level package file */
1899   if (strSuffixLower(basename, ".xsb") &&
1900       strchr(basename, '%') == NULL)
1901     return LEVEL_FILE_TYPE_SB;
1902
1903   /* ---------- try to determine file type from filesize ---------- */
1904
1905   checked_free(filename);
1906   filename = getPath2(getCurrentLevelDir(), basename);
1907
1908   if (stat(filename, &file_status) == 0)
1909   {
1910     /* check for typical filesize of a Supaplex level package file */
1911     if (file_status.st_size == 170496)
1912       return LEVEL_FILE_TYPE_SP;
1913   }
1914
1915   return LEVEL_FILE_TYPE_UNKNOWN;
1916 }
1917
1918 static boolean checkForPackageFromBasename(char *basename)
1919 {
1920   /* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
1921      !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!! */
1922
1923   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
1924 }
1925
1926 static char *getSingleLevelBasenameExt(int nr, char *extension)
1927 {
1928   static char basename[MAX_FILENAME_LEN];
1929
1930   if (nr < 0)
1931     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
1932   else
1933     sprintf(basename, "%03d.%s", nr, extension);
1934
1935   return basename;
1936 }
1937
1938 static char *getSingleLevelBasename(int nr)
1939 {
1940   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
1941 }
1942
1943 static char *getPackedLevelBasename(int type)
1944 {
1945   static char basename[MAX_FILENAME_LEN];
1946   char *directory = getCurrentLevelDir();
1947   Directory *dir;
1948   DirectoryEntry *dir_entry;
1949
1950   strcpy(basename, UNDEFINED_FILENAME);         /* default: undefined file */
1951
1952   if ((dir = openDirectory(directory)) == NULL)
1953   {
1954     Error(ERR_WARN, "cannot read current level directory '%s'", directory);
1955
1956     return basename;
1957   }
1958
1959   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
1960   {
1961     char *entry_basename = dir_entry->basename;
1962     int entry_type = getFileTypeFromBasename(entry_basename);
1963
1964     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  /* found valid level package */
1965     {
1966       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
1967           type == entry_type)
1968       {
1969         strcpy(basename, entry_basename);
1970
1971         break;
1972       }
1973     }
1974   }
1975
1976   closeDirectory(dir);
1977
1978   return basename;
1979 }
1980
1981 static char *getSingleLevelFilename(int nr)
1982 {
1983   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
1984 }
1985
1986 #if ENABLE_UNUSED_CODE
1987 static char *getPackedLevelFilename(int type)
1988 {
1989   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
1990 }
1991 #endif
1992
1993 char *getDefaultLevelFilename(int nr)
1994 {
1995   return getSingleLevelFilename(nr);
1996 }
1997
1998 #if ENABLE_UNUSED_CODE
1999 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2000                                                  int type)
2001 {
2002   lfi->type = type;
2003   lfi->packed = FALSE;
2004   lfi->basename = getSingleLevelBasename(lfi->nr, lfi->type);
2005   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2006 }
2007 #endif
2008
2009 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2010                                                  int type, char *format, ...)
2011 {
2012   static char basename[MAX_FILENAME_LEN];
2013   va_list ap;
2014
2015   va_start(ap, format);
2016   vsprintf(basename, format, ap);
2017   va_end(ap);
2018
2019   lfi->type = type;
2020   lfi->packed = FALSE;
2021   lfi->basename = basename;
2022   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2023 }
2024
2025 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2026                                                  int type)
2027 {
2028   lfi->type = type;
2029   lfi->packed = TRUE;
2030   lfi->basename = getPackedLevelBasename(lfi->type);
2031   lfi->filename = getLevelFilenameFromBasename(lfi->basename);
2032 }
2033
2034 static int getFiletypeFromID(char *filetype_id)
2035 {
2036   char *filetype_id_lower;
2037   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2038   int i;
2039
2040   if (filetype_id == NULL)
2041     return LEVEL_FILE_TYPE_UNKNOWN;
2042
2043   filetype_id_lower = getStringToLower(filetype_id);
2044
2045   for (i = 0; filetype_id_list[i].id != NULL; i++)
2046   {
2047     char *id_lower = getStringToLower(filetype_id_list[i].id);
2048     
2049     if (strEqual(filetype_id_lower, id_lower))
2050       filetype = filetype_id_list[i].filetype;
2051
2052     free(id_lower);
2053
2054     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2055       break;
2056   }
2057
2058   free(filetype_id_lower);
2059
2060   return filetype;
2061 }
2062
2063 char *getLocalLevelTemplateFilename()
2064 {
2065   return getDefaultLevelFilename(-1);
2066 }
2067
2068 char *getGlobalLevelTemplateFilename()
2069 {
2070   /* global variable "leveldir_current" must be modified in the loop below */
2071   LevelDirTree *leveldir_current_last = leveldir_current;
2072   char *filename = NULL;
2073
2074   /* check for template level in path from current to topmost tree node */
2075
2076   while (leveldir_current != NULL)
2077   {
2078     filename = getDefaultLevelFilename(-1);
2079
2080     if (fileExists(filename))
2081       break;
2082
2083     leveldir_current = leveldir_current->node_parent;
2084   }
2085
2086   /* restore global variable "leveldir_current" modified in above loop */
2087   leveldir_current = leveldir_current_last;
2088
2089   return filename;
2090 }
2091
2092 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2093 {
2094   int nr = lfi->nr;
2095
2096   /* special case: level number is negative => check for level template file */
2097   if (nr < 0)
2098   {
2099     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2100                                          getSingleLevelBasename(-1));
2101
2102     /* replace local level template filename with global template filename */
2103     lfi->filename = getGlobalLevelTemplateFilename();
2104
2105     /* no fallback if template file not existing */
2106     return;
2107   }
2108
2109   /* special case: check for file name/pattern specified in "levelinfo.conf" */
2110   if (leveldir_current->level_filename != NULL)
2111   {
2112     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2113
2114     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2115                                          leveldir_current->level_filename, nr);
2116
2117     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2118
2119     if (fileExists(lfi->filename))
2120       return;
2121   }
2122
2123   /* check for native Rocks'n'Diamonds level file */
2124   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2125                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2126   if (fileExists(lfi->filename))
2127     return;
2128
2129   /* check for Emerald Mine level file (V1) */
2130   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2131                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2132   if (fileExists(lfi->filename))
2133     return;
2134   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2135                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2136   if (fileExists(lfi->filename))
2137     return;
2138
2139   /* check for Emerald Mine level file (V2 to V5) */
2140   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2141   if (fileExists(lfi->filename))
2142     return;
2143
2144   /* check for Emerald Mine level file (V6 / single mode) */
2145   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2146   if (fileExists(lfi->filename))
2147     return;
2148   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2149   if (fileExists(lfi->filename))
2150     return;
2151
2152   /* check for Emerald Mine level file (V6 / teamwork mode) */
2153   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2154   if (fileExists(lfi->filename))
2155     return;
2156   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2157   if (fileExists(lfi->filename))
2158     return;
2159
2160   /* check for various packed level file formats */
2161   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2162   if (fileExists(lfi->filename))
2163     return;
2164
2165   /* no known level file found -- use default values (and fail later) */
2166   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2167                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2168 }
2169
2170 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2171 {
2172   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2173     lfi->type = getFileTypeFromBasename(lfi->basename);
2174 }
2175
2176 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2177 {
2178   /* always start with reliable default values */
2179   setFileInfoToDefaults(level_file_info);
2180
2181   level_file_info->nr = nr;     /* set requested level number */
2182
2183   determineLevelFileInfo_Filename(level_file_info);
2184   determineLevelFileInfo_Filetype(level_file_info);
2185 }
2186
2187 /* ------------------------------------------------------------------------- */
2188 /* functions for loading R'n'D level                                         */
2189 /* ------------------------------------------------------------------------- */
2190
2191 int getMappedElement(int element)
2192 {
2193   /* remap some (historic, now obsolete) elements */
2194
2195   switch (element)
2196   {
2197     case EL_PLAYER_OBSOLETE:
2198       element = EL_PLAYER_1;
2199       break;
2200
2201     case EL_KEY_OBSOLETE:
2202       element = EL_KEY_1;
2203       break;
2204
2205     case EL_EM_KEY_1_FILE_OBSOLETE:
2206       element = EL_EM_KEY_1;
2207       break;
2208
2209     case EL_EM_KEY_2_FILE_OBSOLETE:
2210       element = EL_EM_KEY_2;
2211       break;
2212
2213     case EL_EM_KEY_3_FILE_OBSOLETE:
2214       element = EL_EM_KEY_3;
2215       break;
2216
2217     case EL_EM_KEY_4_FILE_OBSOLETE:
2218       element = EL_EM_KEY_4;
2219       break;
2220
2221     case EL_ENVELOPE_OBSOLETE:
2222       element = EL_ENVELOPE_1;
2223       break;
2224
2225     case EL_SP_EMPTY:
2226       element = EL_EMPTY;
2227       break;
2228
2229     default:
2230       if (element >= NUM_FILE_ELEMENTS)
2231       {
2232         Error(ERR_WARN, "invalid level element %d", element);
2233
2234         element = EL_UNKNOWN;
2235       }
2236       break;
2237   }
2238
2239   return element;
2240 }
2241
2242 int getMappedElementByVersion(int element, int game_version)
2243 {
2244   /* remap some elements due to certain game version */
2245
2246   if (game_version <= VERSION_IDENT(2,2,0,0))
2247   {
2248     /* map game font elements */
2249     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2250                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2251                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2252                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2253   }
2254
2255   if (game_version < VERSION_IDENT(3,0,0,0))
2256   {
2257     /* map Supaplex gravity tube elements */
2258     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2259                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2260                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2261                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2262                element);
2263   }
2264
2265   return element;
2266 }
2267
2268 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2269 {
2270   level->file_version = getFileVersion(file);
2271   level->game_version = getFileVersion(file);
2272
2273   return chunk_size;
2274 }
2275
2276 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2277 {
2278   level->creation_date.year  = getFile16BitBE(file);
2279   level->creation_date.month = getFile8Bit(file);
2280   level->creation_date.day   = getFile8Bit(file);
2281
2282   level->creation_date.src   = DATE_SRC_LEVELFILE;
2283
2284   return chunk_size;
2285 }
2286
2287 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2288 {
2289   int initial_player_stepsize;
2290   int initial_player_gravity;
2291   int i, x, y;
2292
2293   level->fieldx = getFile8Bit(file);
2294   level->fieldy = getFile8Bit(file);
2295
2296   level->time           = getFile16BitBE(file);
2297   level->gems_needed    = getFile16BitBE(file);
2298
2299   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2300     level->name[i] = getFile8Bit(file);
2301   level->name[MAX_LEVEL_NAME_LEN] = 0;
2302
2303   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2304     level->score[i] = getFile8Bit(file);
2305
2306   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2307   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2308     for (y = 0; y < 3; y++)
2309       for (x = 0; x < 3; x++)
2310         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2311
2312   level->amoeba_speed           = getFile8Bit(file);
2313   level->time_magic_wall        = getFile8Bit(file);
2314   level->time_wheel             = getFile8Bit(file);
2315   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2316
2317   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2318                                    STEPSIZE_NORMAL);
2319
2320   for (i = 0; i < MAX_PLAYERS; i++)
2321     level->initial_player_stepsize[i] = initial_player_stepsize;
2322
2323   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2324
2325   for (i = 0; i < MAX_PLAYERS; i++)
2326     level->initial_player_gravity[i] = initial_player_gravity;
2327
2328   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2329   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2330
2331   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2332
2333   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2334   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2335   level->can_move_into_acid_bits = getFile32BitBE(file);
2336   level->dont_collide_with_bits = getFile8Bit(file);
2337
2338   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2339   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2340
2341   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2342   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2343   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2344
2345   level->game_engine_type       = getFile8Bit(file);
2346
2347   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
2348
2349   return chunk_size;
2350 }
2351
2352 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
2353 {
2354   int i;
2355
2356   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2357     level->name[i] = getFile8Bit(file);
2358   level->name[MAX_LEVEL_NAME_LEN] = 0;
2359
2360   return chunk_size;
2361 }
2362
2363 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
2364 {
2365   int i;
2366
2367   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2368     level->author[i] = getFile8Bit(file);
2369   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
2370
2371   return chunk_size;
2372 }
2373
2374 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
2375 {
2376   int x, y;
2377   int chunk_size_expected = level->fieldx * level->fieldy;
2378
2379   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2380      stored with 16-bit encoding (and should be twice as big then).
2381      Even worse, playfield data was stored 16-bit when only yamyam content
2382      contained 16-bit elements and vice versa. */
2383
2384   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2385     chunk_size_expected *= 2;
2386
2387   if (chunk_size_expected != chunk_size)
2388   {
2389     ReadUnusedBytesFromFile(file, chunk_size);
2390     return chunk_size_expected;
2391   }
2392
2393   for (y = 0; y < level->fieldy; y++)
2394     for (x = 0; x < level->fieldx; x++)
2395       level->field[x][y] =
2396         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
2397                          getFile8Bit(file));
2398   return chunk_size;
2399 }
2400
2401 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
2402 {
2403   int i, x, y;
2404   int header_size = 4;
2405   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
2406   int chunk_size_expected = header_size + content_size;
2407
2408   /* Note: "chunk_size" was wrong before version 2.0 when elements are
2409      stored with 16-bit encoding (and should be twice as big then).
2410      Even worse, playfield data was stored 16-bit when only yamyam content
2411      contained 16-bit elements and vice versa. */
2412
2413   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
2414     chunk_size_expected += content_size;
2415
2416   if (chunk_size_expected != chunk_size)
2417   {
2418     ReadUnusedBytesFromFile(file, chunk_size);
2419     return chunk_size_expected;
2420   }
2421
2422   getFile8Bit(file);
2423   level->num_yamyam_contents = getFile8Bit(file);
2424   getFile8Bit(file);
2425   getFile8Bit(file);
2426
2427   /* correct invalid number of content fields -- should never happen */
2428   if (level->num_yamyam_contents < 1 ||
2429       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
2430     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2431
2432   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2433     for (y = 0; y < 3; y++)
2434       for (x = 0; x < 3; x++)
2435         level->yamyam_content[i].e[x][y] =
2436           getMappedElement(level->encoding_16bit_field ?
2437                            getFile16BitBE(file) : getFile8Bit(file));
2438   return chunk_size;
2439 }
2440
2441 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
2442 {
2443   int i, x, y;
2444   int element;
2445   int num_contents;
2446   int content_array[MAX_ELEMENT_CONTENTS][3][3];
2447
2448   element = getMappedElement(getFile16BitBE(file));
2449   num_contents = getFile8Bit(file);
2450
2451   getFile8Bit(file);    /* content x size (unused) */
2452   getFile8Bit(file);    /* content y size (unused) */
2453
2454   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
2455
2456   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
2457     for (y = 0; y < 3; y++)
2458       for (x = 0; x < 3; x++)
2459         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
2460
2461   /* correct invalid number of content fields -- should never happen */
2462   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
2463     num_contents = STD_ELEMENT_CONTENTS;
2464
2465   if (element == EL_YAMYAM)
2466   {
2467     level->num_yamyam_contents = num_contents;
2468
2469     for (i = 0; i < num_contents; i++)
2470       for (y = 0; y < 3; y++)
2471         for (x = 0; x < 3; x++)
2472           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
2473   }
2474   else if (element == EL_BD_AMOEBA)
2475   {
2476     level->amoeba_content = content_array[0][0][0];
2477   }
2478   else
2479   {
2480     Error(ERR_WARN, "cannot load content for element '%d'", element);
2481   }
2482
2483   return chunk_size;
2484 }
2485
2486 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
2487 {
2488   int i;
2489   int element;
2490   int envelope_nr;
2491   int envelope_len;
2492   int chunk_size_expected;
2493
2494   element = getMappedElement(getFile16BitBE(file));
2495   if (!IS_ENVELOPE(element))
2496     element = EL_ENVELOPE_1;
2497
2498   envelope_nr = element - EL_ENVELOPE_1;
2499
2500   envelope_len = getFile16BitBE(file);
2501
2502   level->envelope[envelope_nr].xsize = getFile8Bit(file);
2503   level->envelope[envelope_nr].ysize = getFile8Bit(file);
2504
2505   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
2506
2507   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
2508   if (chunk_size_expected != chunk_size)
2509   {
2510     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
2511     return chunk_size_expected;
2512   }
2513
2514   for (i = 0; i < envelope_len; i++)
2515     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
2516
2517   return chunk_size;
2518 }
2519
2520 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
2521 {
2522   int num_changed_custom_elements = getFile16BitBE(file);
2523   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
2524   int i;
2525
2526   if (chunk_size_expected != chunk_size)
2527   {
2528     ReadUnusedBytesFromFile(file, chunk_size - 2);
2529     return chunk_size_expected;
2530   }
2531
2532   for (i = 0; i < num_changed_custom_elements; i++)
2533   {
2534     int element = getMappedElement(getFile16BitBE(file));
2535     int properties = getFile32BitBE(file);
2536
2537     if (IS_CUSTOM_ELEMENT(element))
2538       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
2539     else
2540       Error(ERR_WARN, "invalid custom element number %d", element);
2541
2542     /* older game versions that wrote level files with CUS1 chunks used
2543        different default push delay values (not yet stored in level file) */
2544     element_info[element].push_delay_fixed = 2;
2545     element_info[element].push_delay_random = 8;
2546   }
2547
2548   return chunk_size;
2549 }
2550
2551 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
2552 {
2553   int num_changed_custom_elements = getFile16BitBE(file);
2554   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
2555   int i;
2556
2557   if (chunk_size_expected != chunk_size)
2558   {
2559     ReadUnusedBytesFromFile(file, chunk_size - 2);
2560     return chunk_size_expected;
2561   }
2562
2563   for (i = 0; i < num_changed_custom_elements; i++)
2564   {
2565     int element = getMappedElement(getFile16BitBE(file));
2566     int custom_target_element = getMappedElement(getFile16BitBE(file));
2567
2568     if (IS_CUSTOM_ELEMENT(element))
2569       element_info[element].change->target_element = custom_target_element;
2570     else
2571       Error(ERR_WARN, "invalid custom element number %d", element);
2572   }
2573
2574   return chunk_size;
2575 }
2576
2577 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
2578 {
2579   int num_changed_custom_elements = getFile16BitBE(file);
2580   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
2581   int i, j, x, y;
2582
2583   if (chunk_size_expected != chunk_size)
2584   {
2585     ReadUnusedBytesFromFile(file, chunk_size - 2);
2586     return chunk_size_expected;
2587   }
2588
2589   for (i = 0; i < num_changed_custom_elements; i++)
2590   {
2591     int element = getMappedElement(getFile16BitBE(file));
2592     struct ElementInfo *ei = &element_info[element];
2593     unsigned int event_bits;
2594
2595     if (!IS_CUSTOM_ELEMENT(element))
2596     {
2597       Error(ERR_WARN, "invalid custom element number %d", element);
2598
2599       element = EL_INTERNAL_DUMMY;
2600     }
2601
2602     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
2603       ei->description[j] = getFile8Bit(file);
2604     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2605
2606     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2607
2608     /* some free bytes for future properties and padding */
2609     ReadUnusedBytesFromFile(file, 7);
2610
2611     ei->use_gfx_element = getFile8Bit(file);
2612     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2613
2614     ei->collect_score_initial = getFile8Bit(file);
2615     ei->collect_count_initial = getFile8Bit(file);
2616
2617     ei->push_delay_fixed = getFile16BitBE(file);
2618     ei->push_delay_random = getFile16BitBE(file);
2619     ei->move_delay_fixed = getFile16BitBE(file);
2620     ei->move_delay_random = getFile16BitBE(file);
2621
2622     ei->move_pattern = getFile16BitBE(file);
2623     ei->move_direction_initial = getFile8Bit(file);
2624     ei->move_stepsize = getFile8Bit(file);
2625
2626     for (y = 0; y < 3; y++)
2627       for (x = 0; x < 3; x++)
2628         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2629
2630     event_bits = getFile32BitBE(file);
2631     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2632       if (event_bits & (1 << j))
2633         ei->change->has_event[j] = TRUE;
2634
2635     ei->change->target_element = getMappedElement(getFile16BitBE(file));
2636
2637     ei->change->delay_fixed = getFile16BitBE(file);
2638     ei->change->delay_random = getFile16BitBE(file);
2639     ei->change->delay_frames = getFile16BitBE(file);
2640
2641     ei->change->initial_trigger_element= getMappedElement(getFile16BitBE(file));
2642
2643     ei->change->explode = getFile8Bit(file);
2644     ei->change->use_target_content = getFile8Bit(file);
2645     ei->change->only_if_complete = getFile8Bit(file);
2646     ei->change->use_random_replace = getFile8Bit(file);
2647
2648     ei->change->random_percentage = getFile8Bit(file);
2649     ei->change->replace_when = getFile8Bit(file);
2650
2651     for (y = 0; y < 3; y++)
2652       for (x = 0; x < 3; x++)
2653         ei->change->target_content.e[x][y] =
2654           getMappedElement(getFile16BitBE(file));
2655
2656     ei->slippery_type = getFile8Bit(file);
2657
2658     /* some free bytes for future properties and padding */
2659     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
2660
2661     /* mark that this custom element has been modified */
2662     ei->modified_settings = TRUE;
2663   }
2664
2665   return chunk_size;
2666 }
2667
2668 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
2669 {
2670   struct ElementInfo *ei;
2671   int chunk_size_expected;
2672   int element;
2673   int i, j, x, y;
2674
2675   /* ---------- custom element base property values (96 bytes) ------------- */
2676
2677   element = getMappedElement(getFile16BitBE(file));
2678
2679   if (!IS_CUSTOM_ELEMENT(element))
2680   {
2681     Error(ERR_WARN, "invalid custom element number %d", element);
2682
2683     ReadUnusedBytesFromFile(file, chunk_size - 2);
2684     return chunk_size;
2685   }
2686
2687   ei = &element_info[element];
2688
2689   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2690     ei->description[i] = getFile8Bit(file);
2691   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2692
2693   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
2694
2695   ReadUnusedBytesFromFile(file, 4);     /* reserved for more base properties */
2696
2697   ei->num_change_pages = getFile8Bit(file);
2698
2699   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
2700   if (chunk_size_expected != chunk_size)
2701   {
2702     ReadUnusedBytesFromFile(file, chunk_size - 43);
2703     return chunk_size_expected;
2704   }
2705
2706   ei->ce_value_fixed_initial = getFile16BitBE(file);
2707   ei->ce_value_random_initial = getFile16BitBE(file);
2708   ei->use_last_ce_value = getFile8Bit(file);
2709
2710   ei->use_gfx_element = getFile8Bit(file);
2711   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2712
2713   ei->collect_score_initial = getFile8Bit(file);
2714   ei->collect_count_initial = getFile8Bit(file);
2715
2716   ei->drop_delay_fixed = getFile8Bit(file);
2717   ei->push_delay_fixed = getFile8Bit(file);
2718   ei->drop_delay_random = getFile8Bit(file);
2719   ei->push_delay_random = getFile8Bit(file);
2720   ei->move_delay_fixed = getFile16BitBE(file);
2721   ei->move_delay_random = getFile16BitBE(file);
2722
2723   /* bits 0 - 15 of "move_pattern" ... */
2724   ei->move_pattern = getFile16BitBE(file);
2725   ei->move_direction_initial = getFile8Bit(file);
2726   ei->move_stepsize = getFile8Bit(file);
2727
2728   ei->slippery_type = getFile8Bit(file);
2729
2730   for (y = 0; y < 3; y++)
2731     for (x = 0; x < 3; x++)
2732       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
2733
2734   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
2735   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
2736   ei->move_leave_type = getFile8Bit(file);
2737
2738   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
2739   ei->move_pattern |= (getFile16BitBE(file) << 16);
2740
2741   ei->access_direction = getFile8Bit(file);
2742
2743   ei->explosion_delay = getFile8Bit(file);
2744   ei->ignition_delay = getFile8Bit(file);
2745   ei->explosion_type = getFile8Bit(file);
2746
2747   /* some free bytes for future custom property values and padding */
2748   ReadUnusedBytesFromFile(file, 1);
2749
2750   /* ---------- change page property values (48 bytes) --------------------- */
2751
2752   setElementChangePages(ei, ei->num_change_pages);
2753
2754   for (i = 0; i < ei->num_change_pages; i++)
2755   {
2756     struct ElementChangeInfo *change = &ei->change_page[i];
2757     unsigned int event_bits;
2758
2759     /* always start with reliable default values */
2760     setElementChangeInfoToDefaults(change);
2761
2762     /* bits 0 - 31 of "has_event[]" ... */
2763     event_bits = getFile32BitBE(file);
2764     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
2765       if (event_bits & (1 << j))
2766         change->has_event[j] = TRUE;
2767
2768     change->target_element = getMappedElement(getFile16BitBE(file));
2769
2770     change->delay_fixed = getFile16BitBE(file);
2771     change->delay_random = getFile16BitBE(file);
2772     change->delay_frames = getFile16BitBE(file);
2773
2774     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
2775
2776     change->explode = getFile8Bit(file);
2777     change->use_target_content = getFile8Bit(file);
2778     change->only_if_complete = getFile8Bit(file);
2779     change->use_random_replace = getFile8Bit(file);
2780
2781     change->random_percentage = getFile8Bit(file);
2782     change->replace_when = getFile8Bit(file);
2783
2784     for (y = 0; y < 3; y++)
2785       for (x = 0; x < 3; x++)
2786         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
2787
2788     change->can_change = getFile8Bit(file);
2789
2790     change->trigger_side = getFile8Bit(file);
2791
2792     change->trigger_player = getFile8Bit(file);
2793     change->trigger_page = getFile8Bit(file);
2794
2795     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
2796                             CH_PAGE_ANY : (1 << change->trigger_page));
2797
2798     change->has_action = getFile8Bit(file);
2799     change->action_type = getFile8Bit(file);
2800     change->action_mode = getFile8Bit(file);
2801     change->action_arg = getFile16BitBE(file);
2802
2803     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
2804     event_bits = getFile8Bit(file);
2805     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
2806       if (event_bits & (1 << (j - 32)))
2807         change->has_event[j] = TRUE;
2808   }
2809
2810   /* mark this custom element as modified */
2811   ei->modified_settings = TRUE;
2812
2813   return chunk_size;
2814 }
2815
2816 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
2817 {
2818   struct ElementInfo *ei;
2819   struct ElementGroupInfo *group;
2820   int element;
2821   int i;
2822
2823   element = getMappedElement(getFile16BitBE(file));
2824
2825   if (!IS_GROUP_ELEMENT(element))
2826   {
2827     Error(ERR_WARN, "invalid group element number %d", element);
2828
2829     ReadUnusedBytesFromFile(file, chunk_size - 2);
2830     return chunk_size;
2831   }
2832
2833   ei = &element_info[element];
2834
2835   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
2836     ei->description[i] = getFile8Bit(file);
2837   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
2838
2839   group = element_info[element].group;
2840
2841   group->num_elements = getFile8Bit(file);
2842
2843   ei->use_gfx_element = getFile8Bit(file);
2844   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
2845
2846   group->choice_mode = getFile8Bit(file);
2847
2848   /* some free bytes for future values and padding */
2849   ReadUnusedBytesFromFile(file, 3);
2850
2851   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
2852     group->element[i] = getMappedElement(getFile16BitBE(file));
2853
2854   /* mark this group element as modified */
2855   element_info[element].modified_settings = TRUE;
2856
2857   return chunk_size;
2858 }
2859
2860 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
2861                                 int element, int real_element)
2862 {
2863   int micro_chunk_size = 0;
2864   int conf_type = getFile8Bit(file);
2865   int byte_mask = conf_type & CONF_MASK_BYTES;
2866   boolean element_found = FALSE;
2867   int i;
2868
2869   micro_chunk_size += 1;
2870
2871   if (byte_mask == CONF_MASK_MULTI_BYTES)
2872   {
2873     int num_bytes = getFile16BitBE(file);
2874     byte *buffer = checked_malloc(num_bytes);
2875
2876     ReadBytesFromFile(file, buffer, num_bytes);
2877
2878     for (i = 0; conf[i].data_type != -1; i++)
2879     {
2880       if (conf[i].element == element &&
2881           conf[i].conf_type == conf_type)
2882       {
2883         int data_type = conf[i].data_type;
2884         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
2885         int max_num_entities = conf[i].max_num_entities;
2886
2887         if (num_entities > max_num_entities)
2888         {
2889           Error(ERR_WARN,
2890                 "truncating number of entities for element %d from %d to %d",
2891                 element, num_entities, max_num_entities);
2892
2893           num_entities = max_num_entities;
2894         }
2895
2896         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
2897                                   data_type == TYPE_CONTENT_LIST))
2898         {
2899           /* for element and content lists, zero entities are not allowed */
2900           Error(ERR_WARN, "found empty list of entities for element %d",
2901                 element);
2902
2903           /* do not set "num_entities" here to prevent reading behind buffer */
2904
2905           *(int *)(conf[i].num_entities) = 1;   /* at least one is required */
2906         }
2907         else
2908         {
2909           *(int *)(conf[i].num_entities) = num_entities;
2910         }
2911
2912         element_found = TRUE;
2913
2914         if (data_type == TYPE_STRING)
2915         {
2916           char *string = (char *)(conf[i].value);
2917           int j;
2918
2919           for (j = 0; j < max_num_entities; j++)
2920             string[j] = (j < num_entities ? buffer[j] : '\0');
2921         }
2922         else if (data_type == TYPE_ELEMENT_LIST)
2923         {
2924           int *element_array = (int *)(conf[i].value);
2925           int j;
2926
2927           for (j = 0; j < num_entities; j++)
2928             element_array[j] =
2929               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
2930         }
2931         else if (data_type == TYPE_CONTENT_LIST)
2932         {
2933           struct Content *content= (struct Content *)(conf[i].value);
2934           int c, x, y;
2935
2936           for (c = 0; c < num_entities; c++)
2937             for (y = 0; y < 3; y++)
2938               for (x = 0; x < 3; x++)
2939                 content[c].e[x][y] =
2940                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
2941         }
2942         else
2943           element_found = FALSE;
2944
2945         break;
2946       }
2947     }
2948
2949     checked_free(buffer);
2950
2951     micro_chunk_size += 2 + num_bytes;
2952   }
2953   else          /* constant size configuration data (1, 2 or 4 bytes) */
2954   {
2955     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
2956                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
2957                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
2958
2959     for (i = 0; conf[i].data_type != -1; i++)
2960     {
2961       if (conf[i].element == element &&
2962           conf[i].conf_type == conf_type)
2963       {
2964         int data_type = conf[i].data_type;
2965
2966         if (data_type == TYPE_ELEMENT)
2967           value = getMappedElement(value);
2968
2969         if (data_type == TYPE_BOOLEAN)
2970           *(boolean *)(conf[i].value) = value;
2971         else
2972           *(int *)    (conf[i].value) = value;
2973
2974         element_found = TRUE;
2975
2976         break;
2977       }
2978     }
2979
2980     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
2981   }
2982
2983   if (!element_found)
2984   {
2985     char *error_conf_chunk_bytes =
2986       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
2987        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
2988        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
2989     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
2990     int error_element = real_element;
2991
2992     Error(ERR_WARN, "cannot load micro chunk '%s(%d)' value for element %d ['%s']",
2993           error_conf_chunk_bytes, error_conf_chunk_token,
2994           error_element, EL_NAME(error_element));
2995   }
2996
2997   return micro_chunk_size;
2998 }
2999
3000 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3001 {
3002   int real_chunk_size = 0;
3003
3004   li = *level;          /* copy level data into temporary buffer */
3005
3006   while (!checkEndOfFile(file))
3007   {
3008     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3009
3010     if (real_chunk_size >= chunk_size)
3011       break;
3012   }
3013
3014   *level = li;          /* copy temporary buffer back to level data */
3015
3016   return real_chunk_size;
3017 }
3018
3019 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3020 {
3021   int real_chunk_size = 0;
3022
3023   li = *level;          /* copy level data into temporary buffer */
3024
3025   while (!checkEndOfFile(file))
3026   {
3027     int element = getMappedElement(getFile16BitBE(file));
3028
3029     real_chunk_size += 2;
3030     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3031                                             element, element);
3032     if (real_chunk_size >= chunk_size)
3033       break;
3034   }
3035
3036   *level = li;          /* copy temporary buffer back to level data */
3037
3038   return real_chunk_size;
3039 }
3040
3041 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3042 {
3043   int real_chunk_size = 0;
3044
3045   li = *level;          /* copy level data into temporary buffer */
3046
3047   while (!checkEndOfFile(file))
3048   {
3049     int element = getMappedElement(getFile16BitBE(file));
3050
3051     real_chunk_size += 2;
3052     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3053                                             element, element);
3054     if (real_chunk_size >= chunk_size)
3055       break;
3056   }
3057
3058   *level = li;          /* copy temporary buffer back to level data */
3059
3060   return real_chunk_size;
3061 }
3062
3063 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3064 {
3065   int element = getMappedElement(getFile16BitBE(file));
3066   int envelope_nr = element - EL_ENVELOPE_1;
3067   int real_chunk_size = 2;
3068
3069   xx_envelope = level->envelope[envelope_nr];   /* copy into temporary buffer */
3070
3071   while (!checkEndOfFile(file))
3072   {
3073     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3074                                             -1, element);
3075
3076     if (real_chunk_size >= chunk_size)
3077       break;
3078   }
3079
3080   level->envelope[envelope_nr] = xx_envelope;   /* copy from temporary buffer */
3081
3082   return real_chunk_size;
3083 }
3084
3085 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3086 {
3087   int element = getMappedElement(getFile16BitBE(file));
3088   int real_chunk_size = 2;
3089   struct ElementInfo *ei = &element_info[element];
3090   int i;
3091
3092   xx_ei = *ei;          /* copy element data into temporary buffer */
3093
3094   xx_ei.num_change_pages = -1;
3095
3096   while (!checkEndOfFile(file))
3097   {
3098     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3099                                             -1, element);
3100     if (xx_ei.num_change_pages != -1)
3101       break;
3102
3103     if (real_chunk_size >= chunk_size)
3104       break;
3105   }
3106
3107   *ei = xx_ei;
3108
3109   if (ei->num_change_pages == -1)
3110   {
3111     Error(ERR_WARN, "LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3112           EL_NAME(element));
3113
3114     ei->num_change_pages = 1;
3115
3116     setElementChangePages(ei, 1);
3117     setElementChangeInfoToDefaults(ei->change);
3118
3119     return real_chunk_size;
3120   }
3121
3122   /* initialize number of change pages stored for this custom element */
3123   setElementChangePages(ei, ei->num_change_pages);
3124   for (i = 0; i < ei->num_change_pages; i++)
3125     setElementChangeInfoToDefaults(&ei->change_page[i]);
3126
3127   /* start with reading properties for the first change page */
3128   xx_current_change_page = 0;
3129
3130   while (!checkEndOfFile(file))
3131   {
3132     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3133
3134     xx_change = *change;        /* copy change data into temporary buffer */
3135
3136     resetEventBits();           /* reset bits; change page might have changed */
3137
3138     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3139                                             -1, element);
3140
3141     *change = xx_change;
3142
3143     setEventFlagsFromEventBits(change);
3144
3145     if (real_chunk_size >= chunk_size)
3146       break;
3147   }
3148
3149   return real_chunk_size;
3150 }
3151
3152 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3153 {
3154   int element = getMappedElement(getFile16BitBE(file));
3155   int real_chunk_size = 2;
3156   struct ElementInfo *ei = &element_info[element];
3157   struct ElementGroupInfo *group = ei->group;
3158
3159   xx_ei = *ei;          /* copy element data into temporary buffer */
3160   xx_group = *group;    /* copy group data into temporary buffer */
3161
3162   while (!checkEndOfFile(file))
3163   {
3164     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3165                                             -1, element);
3166
3167     if (real_chunk_size >= chunk_size)
3168       break;
3169   }
3170
3171   *ei = xx_ei;
3172   *group = xx_group;
3173
3174   return real_chunk_size;
3175 }
3176
3177 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3178                                       struct LevelFileInfo *level_file_info,
3179                                       boolean level_info_only)
3180 {
3181   char *filename = level_file_info->filename;
3182   char cookie[MAX_LINE_LEN];
3183   char chunk_name[CHUNK_ID_LEN + 1];
3184   int chunk_size;
3185   File *file;
3186
3187   if (!(file = openFile(filename, MODE_READ)))
3188   {
3189     level->no_valid_file = TRUE;
3190     level->no_level_file = TRUE;
3191
3192     if (level_info_only)
3193       return;
3194
3195     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
3196
3197     if (!setup.editor.use_template_for_new_levels)
3198       return;
3199
3200     /* if level file not found, try to initialize level data from template */
3201     filename = getGlobalLevelTemplateFilename();
3202
3203     if (!(file = openFile(filename, MODE_READ)))
3204       return;
3205
3206     /* default: for empty levels, use level template for custom elements */
3207     level->use_custom_template = TRUE;
3208
3209     level->no_valid_file = FALSE;
3210   }
3211
3212   getFileChunkBE(file, chunk_name, NULL);
3213   if (strEqual(chunk_name, "RND1"))
3214   {
3215     getFile32BitBE(file);               /* not used */
3216
3217     getFileChunkBE(file, chunk_name, NULL);
3218     if (!strEqual(chunk_name, "CAVE"))
3219     {
3220       level->no_valid_file = TRUE;
3221
3222       Error(ERR_WARN, "unknown format of level file '%s'", filename);
3223
3224       closeFile(file);
3225
3226       return;
3227     }
3228   }
3229   else  /* check for pre-2.0 file format with cookie string */
3230   {
3231     strcpy(cookie, chunk_name);
3232     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3233       cookie[4] = '\0';
3234     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3235       cookie[strlen(cookie) - 1] = '\0';
3236
3237     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3238     {
3239       level->no_valid_file = TRUE;
3240
3241       Error(ERR_WARN, "unknown format of level file '%s'", filename);
3242
3243       closeFile(file);
3244
3245       return;
3246     }
3247
3248     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3249     {
3250       level->no_valid_file = TRUE;
3251
3252       Error(ERR_WARN, "unsupported version of level file '%s'", filename);
3253
3254       closeFile(file);
3255
3256       return;
3257     }
3258
3259     /* pre-2.0 level files have no game version, so use file version here */
3260     level->game_version = level->file_version;
3261   }
3262
3263   if (level->file_version < FILE_VERSION_1_2)
3264   {
3265     /* level files from versions before 1.2.0 without chunk structure */
3266     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3267     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3268   }
3269   else
3270   {
3271     static struct
3272     {
3273       char *name;
3274       int size;
3275       int (*loader)(File *, int, struct LevelInfo *);
3276     }
3277     chunk_info[] =
3278     {
3279       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3280       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3281       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3282       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3283       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3284       { "INFO", -1,                     LoadLevel_INFO },
3285       { "BODY", -1,                     LoadLevel_BODY },
3286       { "CONT", -1,                     LoadLevel_CONT },
3287       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3288       { "CNT3", -1,                     LoadLevel_CNT3 },
3289       { "CUS1", -1,                     LoadLevel_CUS1 },
3290       { "CUS2", -1,                     LoadLevel_CUS2 },
3291       { "CUS3", -1,                     LoadLevel_CUS3 },
3292       { "CUS4", -1,                     LoadLevel_CUS4 },
3293       { "GRP1", -1,                     LoadLevel_GRP1 },
3294       { "CONF", -1,                     LoadLevel_CONF },
3295       { "ELEM", -1,                     LoadLevel_ELEM },
3296       { "NOTE", -1,                     LoadLevel_NOTE },
3297       { "CUSX", -1,                     LoadLevel_CUSX },
3298       { "GRPX", -1,                     LoadLevel_GRPX },
3299
3300       {  NULL,  0,                      NULL }
3301     };
3302
3303     while (getFileChunkBE(file, chunk_name, &chunk_size))
3304     {
3305       int i = 0;
3306
3307       while (chunk_info[i].name != NULL &&
3308              !strEqual(chunk_name, chunk_info[i].name))
3309         i++;
3310
3311       if (chunk_info[i].name == NULL)
3312       {
3313         Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
3314               chunk_name, filename);
3315         ReadUnusedBytesFromFile(file, chunk_size);
3316       }
3317       else if (chunk_info[i].size != -1 &&
3318                chunk_info[i].size != chunk_size)
3319       {
3320         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3321               chunk_size, chunk_name, filename);
3322         ReadUnusedBytesFromFile(file, chunk_size);
3323       }
3324       else
3325       {
3326         /* call function to load this level chunk */
3327         int chunk_size_expected =
3328           (chunk_info[i].loader)(file, chunk_size, level);
3329
3330         /* the size of some chunks cannot be checked before reading other
3331            chunks first (like "HEAD" and "BODY") that contain some header
3332            information, so check them here */
3333         if (chunk_size_expected != chunk_size)
3334         {
3335           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
3336                 chunk_size, chunk_name, filename);
3337         }
3338       }
3339     }
3340   }
3341
3342   closeFile(file);
3343 }
3344
3345
3346 /* ------------------------------------------------------------------------- */
3347 /* functions for loading EM level                                            */
3348 /* ------------------------------------------------------------------------- */
3349
3350 void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
3351 {
3352   static int ball_xy[8][2] =
3353   {
3354     { 0, 0 },
3355     { 1, 0 },
3356     { 2, 0 },
3357     { 0, 1 },
3358     { 2, 1 },
3359     { 0, 2 },
3360     { 1, 2 },
3361     { 2, 2 },
3362   };
3363   struct LevelInfo_EM *level_em = level->native_em_level;
3364   struct LEVEL *lev = level_em->lev;
3365   struct PLAYER **ply = level_em->ply;
3366   int i, j, x, y;
3367
3368   lev->width  = MIN(level->fieldx, EM_MAX_CAVE_WIDTH);
3369   lev->height = MIN(level->fieldy, EM_MAX_CAVE_HEIGHT);
3370
3371   lev->time_seconds     = level->time;
3372   lev->required_initial = level->gems_needed;
3373
3374   lev->emerald_score    = level->score[SC_EMERALD];
3375   lev->diamond_score    = level->score[SC_DIAMOND];
3376   lev->alien_score      = level->score[SC_ROBOT];
3377   lev->tank_score       = level->score[SC_SPACESHIP];
3378   lev->bug_score        = level->score[SC_BUG];
3379   lev->eater_score      = level->score[SC_YAMYAM];
3380   lev->nut_score        = level->score[SC_NUT];
3381   lev->dynamite_score   = level->score[SC_DYNAMITE];
3382   lev->key_score        = level->score[SC_KEY];
3383   lev->exit_score       = level->score[SC_TIME_BONUS];
3384
3385   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3386     for (y = 0; y < 3; y++)
3387       for (x = 0; x < 3; x++)
3388         lev->eater_array[i][y * 3 + x] =
3389           map_element_RND_to_EM(level->yamyam_content[i].e[x][y]);
3390
3391   lev->amoeba_time              = level->amoeba_speed;
3392   lev->wonderwall_time_initial  = level->time_magic_wall;
3393   lev->wheel_time               = level->time_wheel;
3394
3395   lev->android_move_time        = level->android_move_time;
3396   lev->android_clone_time       = level->android_clone_time;
3397   lev->ball_random              = level->ball_random;
3398   lev->ball_state_initial       = level->ball_state_initial;
3399   lev->ball_time                = level->ball_time;
3400   lev->num_ball_arrays          = level->num_ball_contents;
3401
3402   lev->lenses_score             = level->lenses_score;
3403   lev->magnify_score            = level->magnify_score;
3404   lev->slurp_score              = level->slurp_score;
3405
3406   lev->lenses_time              = level->lenses_time;
3407   lev->magnify_time             = level->magnify_time;
3408
3409   lev->wind_direction_initial =
3410     map_direction_RND_to_EM(level->wind_direction_initial);
3411   lev->wind_cnt_initial = (level->wind_direction_initial != MV_NONE ?
3412                            lev->wind_time : 0);
3413
3414   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3415     for (j = 0; j < 8; j++)
3416       lev->ball_array[i][j] =
3417         map_element_RND_to_EM(level->
3418                               ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]]);
3419
3420   map_android_clone_elements_RND_to_EM(level);
3421
3422   /* first fill the complete playfield with the default border element */
3423   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
3424     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
3425       level_em->cave[x][y] = ZBORDER;
3426
3427   if (BorderElement == EL_STEELWALL)
3428   {
3429     for (y = 0; y < lev->height + 2; y++)
3430       for (x = 0; x < lev->width + 2; x++)
3431         level_em->cave[x + 1][y + 1] = map_element_RND_to_EM(EL_STEELWALL);
3432   }
3433
3434   /* then copy the real level contents from level file into the playfield */
3435   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3436   {
3437     int new_element = map_element_RND_to_EM(level->field[x][y]);
3438     int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3439     int xx = x + 1 + offset;
3440     int yy = y + 1 + offset;
3441
3442     if (level->field[x][y] == EL_AMOEBA_DEAD)
3443       new_element = map_element_RND_to_EM(EL_AMOEBA_WET);
3444
3445     level_em->cave[xx][yy] = new_element;
3446   }
3447
3448   for (i = 0; i < MAX_PLAYERS; i++)
3449   {
3450     ply[i]->x_initial = 0;
3451     ply[i]->y_initial = 0;
3452   }
3453
3454   /* initialize player positions and delete players from the playfield */
3455   for (y = 0; y < lev->height; y++) for (x = 0; x < lev->width; x++)
3456   {
3457     if (ELEM_IS_PLAYER(level->field[x][y]))
3458     {
3459       int player_nr = GET_PLAYER_NR(level->field[x][y]);
3460       int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
3461       int xx = x + 1 + offset;
3462       int yy = y + 1 + offset;
3463
3464       ply[player_nr]->x_initial = xx;
3465       ply[player_nr]->y_initial = yy;
3466
3467       level_em->cave[xx][yy] = map_element_RND_to_EM(EL_EMPTY);
3468     }
3469   }
3470
3471   if (BorderElement == EL_STEELWALL)
3472   {
3473     lev->width  += 2;
3474     lev->height += 2;
3475   }
3476 }
3477
3478 void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
3479 {
3480   static int ball_xy[8][2] =
3481   {
3482     { 0, 0 },
3483     { 1, 0 },
3484     { 2, 0 },
3485     { 0, 1 },
3486     { 2, 1 },
3487     { 0, 2 },
3488     { 1, 2 },
3489     { 2, 2 },
3490   };
3491   struct LevelInfo_EM *level_em = level->native_em_level;
3492   struct LEVEL *lev = level_em->lev;
3493   struct PLAYER **ply = level_em->ply;
3494   int i, j, x, y;
3495
3496   level->fieldx = MIN(lev->width,  MAX_LEV_FIELDX);
3497   level->fieldy = MIN(lev->height, MAX_LEV_FIELDY);
3498
3499   level->time        = lev->time_seconds;
3500   level->gems_needed = lev->required_initial;
3501
3502   sprintf(level->name, "Level %d", level->file_info.nr);
3503
3504   level->score[SC_EMERALD]      = lev->emerald_score;
3505   level->score[SC_DIAMOND]      = lev->diamond_score;
3506   level->score[SC_ROBOT]        = lev->alien_score;
3507   level->score[SC_SPACESHIP]    = lev->tank_score;
3508   level->score[SC_BUG]          = lev->bug_score;
3509   level->score[SC_YAMYAM]       = lev->eater_score;
3510   level->score[SC_NUT]          = lev->nut_score;
3511   level->score[SC_DYNAMITE]     = lev->dynamite_score;
3512   level->score[SC_KEY]          = lev->key_score;
3513   level->score[SC_TIME_BONUS]   = lev->exit_score;
3514
3515   level->num_yamyam_contents = MAX_ELEMENT_CONTENTS;
3516
3517   for (i = 0; i < level->num_yamyam_contents; i++)
3518     for (y = 0; y < 3; y++)
3519       for (x = 0; x < 3; x++)
3520         level->yamyam_content[i].e[x][y] =
3521           map_element_EM_to_RND(lev->eater_array[i][y * 3 + x]);
3522
3523   level->amoeba_speed           = lev->amoeba_time;
3524   level->time_magic_wall        = lev->wonderwall_time_initial;
3525   level->time_wheel             = lev->wheel_time;
3526
3527   level->android_move_time      = lev->android_move_time;
3528   level->android_clone_time     = lev->android_clone_time;
3529   level->ball_random            = lev->ball_random;
3530   level->ball_state_initial     = lev->ball_state_initial;
3531   level->ball_time              = lev->ball_time;
3532   level->num_ball_contents      = lev->num_ball_arrays;
3533
3534   level->lenses_score           = lev->lenses_score;
3535   level->magnify_score          = lev->magnify_score;
3536   level->slurp_score            = lev->slurp_score;
3537
3538   level->lenses_time            = lev->lenses_time;
3539   level->magnify_time           = lev->magnify_time;
3540
3541   level->wind_direction_initial =
3542     map_direction_EM_to_RND(lev->wind_direction_initial);
3543
3544   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3545     for (j = 0; j < 8; j++)
3546       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
3547         map_element_EM_to_RND(lev->ball_array[i][j]);
3548
3549   map_android_clone_elements_EM_to_RND(level);
3550
3551   /* convert the playfield (some elements need special treatment) */
3552   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
3553   {
3554     int new_element = map_element_EM_to_RND(level_em->cave[x + 1][y + 1]);
3555
3556     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
3557       new_element = EL_AMOEBA_DEAD;
3558
3559     level->field[x][y] = new_element;
3560   }
3561
3562   for (i = 0; i < MAX_PLAYERS; i++)
3563   {
3564     /* in case of all players set to the same field, use the first player */
3565     int nr = MAX_PLAYERS - i - 1;
3566     int jx = ply[nr]->x_initial - 1;
3567     int jy = ply[nr]->y_initial - 1;
3568
3569     if (jx != -1 && jy != -1)
3570       level->field[jx][jy] = EL_PLAYER_1 + nr;
3571   }
3572 }
3573
3574
3575 /* ------------------------------------------------------------------------- */
3576 /* functions for loading SP level                                            */
3577 /* ------------------------------------------------------------------------- */
3578
3579 void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
3580 {
3581   struct LevelInfo_SP *level_sp = level->native_sp_level;
3582   LevelInfoType *header = &level_sp->header;
3583   int i, x, y;
3584
3585   level_sp->width  = level->fieldx;
3586   level_sp->height = level->fieldy;
3587
3588   for (x = 0; x < level->fieldx; x++)
3589     for (y = 0; y < level->fieldy; y++)
3590       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
3591
3592   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
3593
3594   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3595     header->LevelTitle[i] = level->name[i];
3596   /* !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!! */
3597
3598   header->InfotronsNeeded = level->gems_needed;
3599
3600   header->SpecialPortCount = 0;
3601
3602   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
3603   {
3604     boolean gravity_port_found = FALSE;
3605     boolean gravity_port_valid = FALSE;
3606     int gravity_port_flag;
3607     int gravity_port_base_element;
3608     int element = level->field[x][y];
3609
3610     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
3611         element <= EL_SP_GRAVITY_ON_PORT_UP)
3612     {
3613       gravity_port_found = TRUE;
3614       gravity_port_valid = TRUE;
3615       gravity_port_flag = 1;
3616       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
3617     }
3618     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
3619              element <= EL_SP_GRAVITY_OFF_PORT_UP)
3620     {
3621       gravity_port_found = TRUE;
3622       gravity_port_valid = TRUE;
3623       gravity_port_flag = 0;
3624       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
3625     }
3626     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
3627              element <= EL_SP_GRAVITY_PORT_UP)
3628     {
3629       /* change R'n'D style gravity inverting special port to normal port
3630          (there are no gravity inverting ports in native Supaplex engine) */
3631
3632       gravity_port_found = TRUE;
3633       gravity_port_valid = FALSE;
3634       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
3635     }
3636
3637     if (gravity_port_found)
3638     {
3639       if (gravity_port_valid &&
3640           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
3641       {
3642         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
3643
3644         port->PortLocation = (y * level->fieldx + x) * 2;
3645         port->Gravity = gravity_port_flag;
3646
3647         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
3648
3649         header->SpecialPortCount++;
3650       }
3651       else
3652       {
3653         /* change special gravity port to normal port */
3654
3655         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
3656       }
3657
3658       level_sp->playfield[x][y] = element - EL_SP_START;
3659     }
3660   }
3661 }
3662
3663 void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
3664 {
3665   struct LevelInfo_SP *level_sp = level->native_sp_level;
3666   LevelInfoType *header = &level_sp->header;
3667   boolean num_invalid_elements = 0;
3668   int i, j, x, y;
3669
3670   level->fieldx = level_sp->width;
3671   level->fieldy = level_sp->height;
3672
3673   for (x = 0; x < level->fieldx; x++)
3674   {
3675     for (y = 0; y < level->fieldy; y++)
3676     {
3677       int element_old = level_sp->playfield[x][y];
3678       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3679
3680       if (element_new == EL_UNKNOWN)
3681       {
3682         num_invalid_elements++;
3683
3684         Error(ERR_DEBUG, "invalid element %d at position %d, %d",
3685               element_old, x, y);
3686       }
3687
3688       level->field[x][y] = element_new;
3689     }
3690   }
3691
3692   if (num_invalid_elements > 0)
3693     Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
3694           (!options.debug ? " (use '--debug' for more details)" : ""));
3695
3696   for (i = 0; i < MAX_PLAYERS; i++)
3697     level->initial_player_gravity[i] =
3698       (header->InitialGravity == 1 ? TRUE : FALSE);
3699
3700   /* skip leading spaces */
3701   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3702     if (header->LevelTitle[i] != ' ')
3703       break;
3704
3705   /* copy level title */
3706   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3707     level->name[j] = header->LevelTitle[i];
3708   level->name[j] = '\0';
3709
3710   /* cut trailing spaces */
3711   for (; j > 0; j--)
3712     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3713       level->name[j - 1] = '\0';
3714
3715   level->gems_needed = header->InfotronsNeeded;
3716
3717   for (i = 0; i < header->SpecialPortCount; i++)
3718   {
3719     SpecialPortType *port = &header->SpecialPort[i];
3720     int port_location = port->PortLocation;
3721     int gravity = port->Gravity;
3722     int port_x, port_y, port_element;
3723
3724     port_x = (port_location / 2) % level->fieldx;
3725     port_y = (port_location / 2) / level->fieldx;
3726
3727     if (port_x < 0 || port_x >= level->fieldx ||
3728         port_y < 0 || port_y >= level->fieldy)
3729     {
3730       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3731             port_x, port_y);
3732
3733       continue;
3734     }
3735
3736     port_element = level->field[port_x][port_y];
3737
3738     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3739         port_element > EL_SP_GRAVITY_PORT_UP)
3740     {
3741       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3742
3743       continue;
3744     }
3745
3746     /* change previous (wrong) gravity inverting special port to either
3747        gravity enabling special port or gravity disabling special port */
3748     level->field[port_x][port_y] +=
3749       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3750        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3751   }
3752
3753   /* change special gravity ports without database entries to normal ports */
3754   for (x = 0; x < level->fieldx; x++)
3755     for (y = 0; y < level->fieldy; y++)
3756       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3757           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3758         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3759
3760   level->time = 0;                      /* no time limit */
3761   level->amoeba_speed = 0;
3762   level->time_magic_wall = 0;
3763   level->time_wheel = 0;
3764   level->amoeba_content = EL_EMPTY;
3765
3766 #if 1
3767   /* original Supaplex does not use score values -- use default values */
3768 #else
3769   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3770     level->score[i] = 0;
3771 #endif
3772
3773   /* there are no yamyams in supaplex levels */
3774   for (i = 0; i < level->num_yamyam_contents; i++)
3775     for (x = 0; x < 3; x++)
3776       for (y = 0; y < 3; y++)
3777         level->yamyam_content[i].e[x][y] = EL_EMPTY;
3778 }
3779
3780 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3781 {
3782   struct LevelInfo_SP *level_sp = level->native_sp_level;
3783   struct DemoInfo_SP *demo = &level_sp->demo;
3784   int i, j;
3785
3786   /* always start with reliable default values */
3787   demo->is_available = FALSE;
3788   demo->length = 0;
3789
3790   if (TAPE_IS_EMPTY(tape))
3791     return;
3792
3793   demo->level_nr = tape.level_nr;       /* (currently not used) */
3794
3795   level_sp->header.DemoRandomSeed = tape.random_seed;
3796
3797   demo->length = 0;
3798
3799   for (i = 0; i < tape.length; i++)
3800   {
3801     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3802     int demo_repeat = tape.pos[i].delay;
3803     int demo_entries = (demo_repeat + 15) / 16;
3804
3805     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3806     {
3807       Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3808             SP_MAX_TAPE_LEN);
3809
3810       break;
3811     }
3812
3813     for (j = 0; j < demo_repeat / 16; j++)
3814       demo->data[demo->length++] = 0xf0 | demo_action;
3815
3816     if (demo_repeat % 16)
3817       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3818   }
3819
3820   demo->is_available = TRUE;
3821 }
3822
3823 static void setTapeInfoToDefaults();
3824
3825 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3826 {
3827   struct LevelInfo_SP *level_sp = level->native_sp_level;
3828   struct DemoInfo_SP *demo = &level_sp->demo;
3829   char *filename = level->file_info.filename;
3830   int i;
3831
3832   /* always start with reliable default values */
3833   setTapeInfoToDefaults();
3834
3835   if (!demo->is_available)
3836     return;
3837
3838   tape.level_nr = demo->level_nr;       /* (currently not used) */
3839   tape.random_seed = level_sp->header.DemoRandomSeed;
3840
3841   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3842
3843   tape.counter = 0;
3844   tape.pos[tape.counter].delay = 0;
3845
3846   for (i = 0; i < demo->length; i++)
3847   {
3848     int demo_action = demo->data[i] & 0x0f;
3849     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3850     int tape_action = map_key_SP_to_RND(demo_action);
3851     int tape_repeat = demo_repeat + 1;
3852     byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3853     boolean success = 0;
3854     int j;
3855
3856     for (j = 0; j < tape_repeat; j++)
3857       success = TapeAddAction(action);
3858
3859     if (!success)
3860     {
3861       Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3862             MAX_TAPE_LEN);
3863
3864       break;
3865     }
3866   }
3867
3868   TapeHaltRecording();
3869 }
3870
3871
3872 /* ------------------------------------------------------------------------- */
3873 /* functions for loading DC level                                            */
3874 /* ------------------------------------------------------------------------- */
3875
3876 #define DC_LEVEL_HEADER_SIZE            344
3877
3878 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3879 {
3880   static int last_data_encoded;
3881   static int offset1;
3882   static int offset2;
3883   int diff;
3884   int diff_hi, diff_lo;
3885   int data_hi, data_lo;
3886   unsigned short data_decoded;
3887
3888   if (init)
3889   {
3890     last_data_encoded = 0;
3891     offset1 = -1;
3892     offset2 = 0;
3893
3894     return 0;
3895   }
3896
3897   diff = data_encoded - last_data_encoded;
3898   diff_hi = diff & ~0xff;
3899   diff_lo = diff &  0xff;
3900
3901   offset2 += diff_lo;
3902
3903   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3904   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3905   data_hi = data_hi & 0xff00;
3906
3907   data_decoded = data_hi | data_lo;
3908
3909   last_data_encoded = data_encoded;
3910
3911   offset1 = (offset1 + 1) % 31;
3912   offset2 = offset2 & 0xff;
3913
3914   return data_decoded;
3915 }
3916
3917 int getMappedElement_DC(int element)
3918 {
3919   switch (element)
3920   {
3921     case 0x0000:
3922       element = EL_ROCK;
3923       break;
3924
3925       /* 0x0117 - 0x036e: (?) */
3926       /* EL_DIAMOND */
3927
3928       /* 0x042d - 0x0684: (?) */
3929       /* EL_EMERALD */
3930
3931     case 0x06f1:
3932       element = EL_NUT;
3933       break;
3934
3935     case 0x074c:
3936       element = EL_BOMB;
3937       break;
3938
3939     case 0x07a4:
3940       element = EL_PEARL;
3941       break;
3942
3943     case 0x0823:
3944       element = EL_CRYSTAL;
3945       break;
3946
3947     case 0x0e77:        /* quicksand (boulder) */
3948       element = EL_QUICKSAND_FAST_FULL;
3949       break;
3950
3951     case 0x0e99:        /* slow quicksand (boulder) */
3952       element = EL_QUICKSAND_FULL;
3953       break;
3954
3955     case 0x0ed2:
3956       element = EL_EM_EXIT_OPEN;
3957       break;
3958
3959     case 0x0ee3:
3960       element = EL_EM_EXIT_CLOSED;
3961       break;
3962
3963     case 0x0eeb:
3964       element = EL_EM_STEEL_EXIT_OPEN;
3965       break;
3966
3967     case 0x0efc:
3968       element = EL_EM_STEEL_EXIT_CLOSED;
3969       break;
3970
3971     case 0x0f4f:        /* dynamite (lit 1) */
3972       element = EL_EM_DYNAMITE_ACTIVE;
3973       break;
3974
3975     case 0x0f57:        /* dynamite (lit 2) */
3976       element = EL_EM_DYNAMITE_ACTIVE;
3977       break;
3978
3979     case 0x0f5f:        /* dynamite (lit 3) */
3980       element = EL_EM_DYNAMITE_ACTIVE;
3981       break;
3982
3983     case 0x0f67:        /* dynamite (lit 4) */
3984       element = EL_EM_DYNAMITE_ACTIVE;
3985       break;
3986
3987     case 0x0f81:
3988     case 0x0f82:
3989     case 0x0f83:
3990     case 0x0f84:
3991       element = EL_AMOEBA_WET;
3992       break;
3993
3994     case 0x0f85:
3995       element = EL_AMOEBA_DROP;
3996       break;
3997
3998     case 0x0fb9:
3999       element = EL_DC_MAGIC_WALL;
4000       break;
4001
4002     case 0x0fd0:
4003       element = EL_SPACESHIP_UP;
4004       break;
4005
4006     case 0x0fd9:
4007       element = EL_SPACESHIP_DOWN;
4008       break;
4009
4010     case 0x0ff1:
4011       element = EL_SPACESHIP_LEFT;
4012       break;
4013
4014     case 0x0ff9:
4015       element = EL_SPACESHIP_RIGHT;
4016       break;
4017
4018     case 0x1057:
4019       element = EL_BUG_UP;
4020       break;
4021
4022     case 0x1060:
4023       element = EL_BUG_DOWN;
4024       break;
4025
4026     case 0x1078:
4027       element = EL_BUG_LEFT;
4028       break;
4029
4030     case 0x1080:
4031       element = EL_BUG_RIGHT;
4032       break;
4033
4034     case 0x10de:
4035       element = EL_MOLE_UP;
4036       break;
4037
4038     case 0x10e7:
4039       element = EL_MOLE_DOWN;
4040       break;
4041
4042     case 0x10ff:
4043       element = EL_MOLE_LEFT;
4044       break;
4045
4046     case 0x1107:
4047       element = EL_MOLE_RIGHT;
4048       break;
4049
4050     case 0x11c0:
4051       element = EL_ROBOT;
4052       break;
4053
4054     case 0x13f5:
4055       element = EL_YAMYAM;
4056       break;
4057
4058     case 0x1425:
4059       element = EL_SWITCHGATE_OPEN;
4060       break;
4061
4062     case 0x1426:
4063       element = EL_SWITCHGATE_CLOSED;
4064       break;
4065
4066     case 0x1437:
4067       element = EL_DC_SWITCHGATE_SWITCH_UP;
4068       break;
4069
4070     case 0x143a:
4071       element = EL_TIMEGATE_CLOSED;
4072       break;
4073
4074     case 0x144c:        /* conveyor belt switch (green) */
4075       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4076       break;
4077
4078     case 0x144f:        /* conveyor belt switch (red) */
4079       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4080       break;
4081
4082     case 0x1452:        /* conveyor belt switch (blue) */
4083       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4084       break;
4085
4086     case 0x145b:
4087       element = EL_CONVEYOR_BELT_3_MIDDLE;
4088       break;
4089
4090     case 0x1463:
4091       element = EL_CONVEYOR_BELT_3_LEFT;
4092       break;
4093
4094     case 0x146b:
4095       element = EL_CONVEYOR_BELT_3_RIGHT;
4096       break;
4097
4098     case 0x1473:
4099       element = EL_CONVEYOR_BELT_1_MIDDLE;
4100       break;
4101
4102     case 0x147b:
4103       element = EL_CONVEYOR_BELT_1_LEFT;
4104       break;
4105
4106     case 0x1483:
4107       element = EL_CONVEYOR_BELT_1_RIGHT;
4108       break;
4109
4110     case 0x148b:
4111       element = EL_CONVEYOR_BELT_4_MIDDLE;
4112       break;
4113
4114     case 0x1493:
4115       element = EL_CONVEYOR_BELT_4_LEFT;
4116       break;
4117
4118     case 0x149b:
4119       element = EL_CONVEYOR_BELT_4_RIGHT;
4120       break;
4121
4122     case 0x14ac:
4123       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4124       break;
4125
4126     case 0x14bd:
4127       element = EL_EXPANDABLE_WALL_VERTICAL;
4128       break;
4129
4130     case 0x14c6:
4131       element = EL_EXPANDABLE_WALL_ANY;
4132       break;
4133
4134     case 0x14ce:        /* growing steel wall (left/right) */
4135       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4136       break;
4137
4138     case 0x14df:        /* growing steel wall (up/down) */
4139       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4140       break;
4141
4142     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4143       element = EL_EXPANDABLE_STEELWALL_ANY;
4144       break;
4145
4146     case 0x14e9:
4147       element = EL_SHIELD_DEADLY;
4148       break;
4149
4150     case 0x1501:
4151       element = EL_EXTRA_TIME;
4152       break;
4153
4154     case 0x154f:
4155       element = EL_ACID;
4156       break;
4157
4158     case 0x1577:
4159       element = EL_EMPTY_SPACE;
4160       break;
4161
4162     case 0x1578:        /* quicksand (empty) */
4163       element = EL_QUICKSAND_FAST_EMPTY;
4164       break;
4165
4166     case 0x1579:        /* slow quicksand (empty) */
4167       element = EL_QUICKSAND_EMPTY;
4168       break;
4169
4170       /* 0x157c - 0x158b: */
4171       /* EL_SAND */
4172
4173       /* 0x1590 - 0x159f: */
4174       /* EL_DC_LANDMINE */
4175
4176     case 0x15a0:
4177       element = EL_EM_DYNAMITE;
4178       break;
4179
4180     case 0x15a1:        /* key (red) */
4181       element = EL_EM_KEY_1;
4182       break;
4183
4184     case 0x15a2:        /* key (yellow) */
4185       element = EL_EM_KEY_2;
4186       break;
4187
4188     case 0x15a3:        /* key (blue) */
4189       element = EL_EM_KEY_4;
4190       break;
4191
4192     case 0x15a4:        /* key (green) */
4193       element = EL_EM_KEY_3;
4194       break;
4195
4196     case 0x15a5:        /* key (white) */
4197       element = EL_DC_KEY_WHITE;
4198       break;
4199
4200     case 0x15a6:
4201       element = EL_WALL_SLIPPERY;
4202       break;
4203
4204     case 0x15a7:
4205       element = EL_WALL;
4206       break;
4207
4208     case 0x15a8:        /* wall (not round) */
4209       element = EL_WALL;
4210       break;
4211
4212     case 0x15a9:        /* (blue) */
4213       element = EL_CHAR_A;
4214       break;
4215
4216     case 0x15aa:        /* (blue) */
4217       element = EL_CHAR_B;
4218       break;
4219
4220     case 0x15ab:        /* (blue) */
4221       element = EL_CHAR_C;
4222       break;
4223
4224     case 0x15ac:        /* (blue) */
4225       element = EL_CHAR_D;
4226       break;
4227
4228     case 0x15ad:        /* (blue) */
4229       element = EL_CHAR_E;
4230       break;
4231
4232     case 0x15ae:        /* (blue) */
4233       element = EL_CHAR_F;
4234       break;
4235
4236     case 0x15af:        /* (blue) */
4237       element = EL_CHAR_G;
4238       break;
4239
4240     case 0x15b0:        /* (blue) */
4241       element = EL_CHAR_H;
4242       break;
4243
4244     case 0x15b1:        /* (blue) */
4245       element = EL_CHAR_I;
4246       break;
4247
4248     case 0x15b2:        /* (blue) */
4249       element = EL_CHAR_J;
4250       break;
4251
4252     case 0x15b3:        /* (blue) */
4253       element = EL_CHAR_K;
4254       break;
4255
4256     case 0x15b4:        /* (blue) */
4257       element = EL_CHAR_L;
4258       break;
4259
4260     case 0x15b5:        /* (blue) */
4261       element = EL_CHAR_M;
4262       break;
4263
4264     case 0x15b6:        /* (blue) */
4265       element = EL_CHAR_N;
4266       break;
4267
4268     case 0x15b7:        /* (blue) */
4269       element = EL_CHAR_O;
4270       break;
4271
4272     case 0x15b8:        /* (blue) */
4273       element = EL_CHAR_P;
4274       break;
4275
4276     case 0x15b9:        /* (blue) */
4277       element = EL_CHAR_Q;
4278       break;
4279
4280     case 0x15ba:        /* (blue) */
4281       element = EL_CHAR_R;
4282       break;
4283
4284     case 0x15bb:        /* (blue) */
4285       element = EL_CHAR_S;
4286       break;
4287
4288     case 0x15bc:        /* (blue) */
4289       element = EL_CHAR_T;
4290       break;
4291
4292     case 0x15bd:        /* (blue) */
4293       element = EL_CHAR_U;
4294       break;
4295
4296     case 0x15be:        /* (blue) */
4297       element = EL_CHAR_V;
4298       break;
4299
4300     case 0x15bf:        /* (blue) */
4301       element = EL_CHAR_W;
4302       break;
4303
4304     case 0x15c0:        /* (blue) */
4305       element = EL_CHAR_X;
4306       break;
4307
4308     case 0x15c1:        /* (blue) */
4309       element = EL_CHAR_Y;
4310       break;
4311
4312     case 0x15c2:        /* (blue) */
4313       element = EL_CHAR_Z;
4314       break;
4315
4316     case 0x15c3:        /* (blue) */
4317       element = EL_CHAR_AUMLAUT;
4318       break;
4319
4320     case 0x15c4:        /* (blue) */
4321       element = EL_CHAR_OUMLAUT;
4322       break;
4323
4324     case 0x15c5:        /* (blue) */
4325       element = EL_CHAR_UUMLAUT;
4326       break;
4327
4328     case 0x15c6:        /* (blue) */
4329       element = EL_CHAR_0;
4330       break;
4331
4332     case 0x15c7:        /* (blue) */
4333       element = EL_CHAR_1;
4334       break;
4335
4336     case 0x15c8:        /* (blue) */
4337       element = EL_CHAR_2;
4338       break;
4339
4340     case 0x15c9:        /* (blue) */
4341       element = EL_CHAR_3;
4342       break;
4343
4344     case 0x15ca:        /* (blue) */
4345       element = EL_CHAR_4;
4346       break;
4347
4348     case 0x15cb:        /* (blue) */
4349       element = EL_CHAR_5;
4350       break;
4351
4352     case 0x15cc:        /* (blue) */
4353       element = EL_CHAR_6;
4354       break;
4355
4356     case 0x15cd:        /* (blue) */
4357       element = EL_CHAR_7;
4358       break;
4359
4360     case 0x15ce:        /* (blue) */
4361       element = EL_CHAR_8;
4362       break;
4363
4364     case 0x15cf:        /* (blue) */
4365       element = EL_CHAR_9;
4366       break;
4367
4368     case 0x15d0:        /* (blue) */
4369       element = EL_CHAR_PERIOD;
4370       break;
4371
4372     case 0x15d1:        /* (blue) */
4373       element = EL_CHAR_EXCLAM;
4374       break;
4375
4376     case 0x15d2:        /* (blue) */
4377       element = EL_CHAR_COLON;
4378       break;
4379
4380     case 0x15d3:        /* (blue) */
4381       element = EL_CHAR_LESS;
4382       break;
4383
4384     case 0x15d4:        /* (blue) */
4385       element = EL_CHAR_GREATER;
4386       break;
4387
4388     case 0x15d5:        /* (blue) */
4389       element = EL_CHAR_QUESTION;
4390       break;
4391
4392     case 0x15d6:        /* (blue) */
4393       element = EL_CHAR_COPYRIGHT;
4394       break;
4395
4396     case 0x15d7:        /* (blue) */
4397       element = EL_CHAR_UP;
4398       break;
4399
4400     case 0x15d8:        /* (blue) */
4401       element = EL_CHAR_DOWN;
4402       break;
4403
4404     case 0x15d9:        /* (blue) */
4405       element = EL_CHAR_BUTTON;
4406       break;
4407
4408     case 0x15da:        /* (blue) */
4409       element = EL_CHAR_PLUS;
4410       break;
4411
4412     case 0x15db:        /* (blue) */
4413       element = EL_CHAR_MINUS;
4414       break;
4415
4416     case 0x15dc:        /* (blue) */
4417       element = EL_CHAR_APOSTROPHE;
4418       break;
4419
4420     case 0x15dd:        /* (blue) */
4421       element = EL_CHAR_PARENLEFT;
4422       break;
4423
4424     case 0x15de:        /* (blue) */
4425       element = EL_CHAR_PARENRIGHT;
4426       break;
4427
4428     case 0x15df:        /* (green) */
4429       element = EL_CHAR_A;
4430       break;
4431
4432     case 0x15e0:        /* (green) */
4433       element = EL_CHAR_B;
4434       break;
4435
4436     case 0x15e1:        /* (green) */
4437       element = EL_CHAR_C;
4438       break;
4439
4440     case 0x15e2:        /* (green) */
4441       element = EL_CHAR_D;
4442       break;
4443
4444     case 0x15e3:        /* (green) */
4445       element = EL_CHAR_E;
4446       break;
4447
4448     case 0x15e4:        /* (green) */
4449       element = EL_CHAR_F;
4450       break;
4451
4452     case 0x15e5:        /* (green) */
4453       element = EL_CHAR_G;
4454       break;
4455
4456     case 0x15e6:        /* (green) */
4457       element = EL_CHAR_H;
4458       break;
4459
4460     case 0x15e7:        /* (green) */
4461       element = EL_CHAR_I;
4462       break;
4463
4464     case 0x15e8:        /* (green) */
4465       element = EL_CHAR_J;
4466       break;
4467
4468     case 0x15e9:        /* (green) */
4469       element = EL_CHAR_K;
4470       break;
4471
4472     case 0x15ea:        /* (green) */
4473       element = EL_CHAR_L;
4474       break;
4475
4476     case 0x15eb:        /* (green) */
4477       element = EL_CHAR_M;
4478       break;
4479
4480     case 0x15ec:        /* (green) */
4481       element = EL_CHAR_N;
4482       break;
4483
4484     case 0x15ed:        /* (green) */
4485       element = EL_CHAR_O;
4486       break;
4487
4488     case 0x15ee:        /* (green) */
4489       element = EL_CHAR_P;
4490       break;
4491
4492     case 0x15ef:        /* (green) */
4493       element = EL_CHAR_Q;
4494       break;
4495
4496     case 0x15f0:        /* (green) */
4497       element = EL_CHAR_R;
4498       break;
4499
4500     case 0x15f1:        /* (green) */
4501       element = EL_CHAR_S;
4502       break;
4503
4504     case 0x15f2:        /* (green) */
4505       element = EL_CHAR_T;
4506       break;
4507
4508     case 0x15f3:        /* (green) */
4509       element = EL_CHAR_U;
4510       break;
4511
4512     case 0x15f4:        /* (green) */
4513       element = EL_CHAR_V;
4514       break;
4515
4516     case 0x15f5:        /* (green) */
4517       element = EL_CHAR_W;
4518       break;
4519
4520     case 0x15f6:        /* (green) */
4521       element = EL_CHAR_X;
4522       break;
4523
4524     case 0x15f7:        /* (green) */
4525       element = EL_CHAR_Y;
4526       break;
4527
4528     case 0x15f8:        /* (green) */
4529       element = EL_CHAR_Z;
4530       break;
4531
4532     case 0x15f9:        /* (green) */
4533       element = EL_CHAR_AUMLAUT;
4534       break;
4535
4536     case 0x15fa:        /* (green) */
4537       element = EL_CHAR_OUMLAUT;
4538       break;
4539
4540     case 0x15fb:        /* (green) */
4541       element = EL_CHAR_UUMLAUT;
4542       break;
4543
4544     case 0x15fc:        /* (green) */
4545       element = EL_CHAR_0;
4546       break;
4547
4548     case 0x15fd:        /* (green) */
4549       element = EL_CHAR_1;
4550       break;
4551
4552     case 0x15fe:        /* (green) */
4553       element = EL_CHAR_2;
4554       break;
4555
4556     case 0x15ff:        /* (green) */
4557       element = EL_CHAR_3;
4558       break;
4559
4560     case 0x1600:        /* (green) */
4561       element = EL_CHAR_4;
4562       break;
4563
4564     case 0x1601:        /* (green) */
4565       element = EL_CHAR_5;
4566       break;
4567
4568     case 0x1602:        /* (green) */
4569       element = EL_CHAR_6;
4570       break;
4571
4572     case 0x1603:        /* (green) */
4573       element = EL_CHAR_7;
4574       break;
4575
4576     case 0x1604:        /* (green) */
4577       element = EL_CHAR_8;
4578       break;
4579
4580     case 0x1605:        /* (green) */
4581       element = EL_CHAR_9;
4582       break;
4583
4584     case 0x1606:        /* (green) */
4585       element = EL_CHAR_PERIOD;
4586       break;
4587
4588     case 0x1607:        /* (green) */
4589       element = EL_CHAR_EXCLAM;
4590       break;
4591
4592     case 0x1608:        /* (green) */
4593       element = EL_CHAR_COLON;
4594       break;
4595
4596     case 0x1609:        /* (green) */
4597       element = EL_CHAR_LESS;
4598       break;
4599
4600     case 0x160a:        /* (green) */
4601       element = EL_CHAR_GREATER;
4602       break;
4603
4604     case 0x160b:        /* (green) */
4605       element = EL_CHAR_QUESTION;
4606       break;
4607
4608     case 0x160c:        /* (green) */
4609       element = EL_CHAR_COPYRIGHT;
4610       break;
4611
4612     case 0x160d:        /* (green) */
4613       element = EL_CHAR_UP;
4614       break;
4615
4616     case 0x160e:        /* (green) */
4617       element = EL_CHAR_DOWN;
4618       break;
4619
4620     case 0x160f:        /* (green) */
4621       element = EL_CHAR_BUTTON;
4622       break;
4623
4624     case 0x1610:        /* (green) */
4625       element = EL_CHAR_PLUS;
4626       break;
4627
4628     case 0x1611:        /* (green) */
4629       element = EL_CHAR_MINUS;
4630       break;
4631
4632     case 0x1612:        /* (green) */
4633       element = EL_CHAR_APOSTROPHE;
4634       break;
4635
4636     case 0x1613:        /* (green) */
4637       element = EL_CHAR_PARENLEFT;
4638       break;
4639
4640     case 0x1614:        /* (green) */
4641       element = EL_CHAR_PARENRIGHT;
4642       break;
4643
4644     case 0x1615:        /* (blue steel) */
4645       element = EL_STEEL_CHAR_A;
4646       break;
4647
4648     case 0x1616:        /* (blue steel) */
4649       element = EL_STEEL_CHAR_B;
4650       break;
4651
4652     case 0x1617:        /* (blue steel) */
4653       element = EL_STEEL_CHAR_C;
4654       break;
4655
4656     case 0x1618:        /* (blue steel) */
4657       element = EL_STEEL_CHAR_D;
4658       break;
4659
4660     case 0x1619:        /* (blue steel) */
4661       element = EL_STEEL_CHAR_E;
4662       break;
4663
4664     case 0x161a:        /* (blue steel) */
4665       element = EL_STEEL_CHAR_F;
4666       break;
4667
4668     case 0x161b:        /* (blue steel) */
4669       element = EL_STEEL_CHAR_G;
4670       break;
4671
4672     case 0x161c:        /* (blue steel) */
4673       element = EL_STEEL_CHAR_H;
4674       break;
4675
4676     case 0x161d:        /* (blue steel) */
4677       element = EL_STEEL_CHAR_I;
4678       break;
4679
4680     case 0x161e:        /* (blue steel) */
4681       element = EL_STEEL_CHAR_J;
4682       break;
4683
4684     case 0x161f:        /* (blue steel) */
4685       element = EL_STEEL_CHAR_K;
4686       break;
4687
4688     case 0x1620:        /* (blue steel) */
4689       element = EL_STEEL_CHAR_L;
4690       break;
4691
4692     case 0x1621:        /* (blue steel) */
4693       element = EL_STEEL_CHAR_M;
4694       break;
4695
4696     case 0x1622:        /* (blue steel) */
4697       element = EL_STEEL_CHAR_N;
4698       break;
4699
4700     case 0x1623:        /* (blue steel) */
4701       element = EL_STEEL_CHAR_O;
4702       break;
4703
4704     case 0x1624:        /* (blue steel) */
4705       element = EL_STEEL_CHAR_P;
4706       break;
4707
4708     case 0x1625:        /* (blue steel) */
4709       element = EL_STEEL_CHAR_Q;
4710       break;
4711
4712     case 0x1626:        /* (blue steel) */
4713       element = EL_STEEL_CHAR_R;
4714       break;
4715
4716     case 0x1627:        /* (blue steel) */
4717       element = EL_STEEL_CHAR_S;
4718       break;
4719
4720     case 0x1628:        /* (blue steel) */
4721       element = EL_STEEL_CHAR_T;
4722       break;
4723
4724     case 0x1629:        /* (blue steel) */
4725       element = EL_STEEL_CHAR_U;
4726       break;
4727
4728     case 0x162a:        /* (blue steel) */
4729       element = EL_STEEL_CHAR_V;
4730       break;
4731
4732     case 0x162b:        /* (blue steel) */
4733       element = EL_STEEL_CHAR_W;
4734       break;
4735
4736     case 0x162c:        /* (blue steel) */
4737       element = EL_STEEL_CHAR_X;
4738       break;
4739
4740     case 0x162d:        /* (blue steel) */
4741       element = EL_STEEL_CHAR_Y;
4742       break;
4743
4744     case 0x162e:        /* (blue steel) */
4745       element = EL_STEEL_CHAR_Z;
4746       break;
4747
4748     case 0x162f:        /* (blue steel) */
4749       element = EL_STEEL_CHAR_AUMLAUT;
4750       break;
4751
4752     case 0x1630:        /* (blue steel) */
4753       element = EL_STEEL_CHAR_OUMLAUT;
4754       break;
4755
4756     case 0x1631:        /* (blue steel) */
4757       element = EL_STEEL_CHAR_UUMLAUT;
4758       break;
4759
4760     case 0x1632:        /* (blue steel) */
4761       element = EL_STEEL_CHAR_0;
4762       break;
4763
4764     case 0x1633:        /* (blue steel) */
4765       element = EL_STEEL_CHAR_1;
4766       break;
4767
4768     case 0x1634:        /* (blue steel) */
4769       element = EL_STEEL_CHAR_2;
4770       break;
4771
4772     case 0x1635:        /* (blue steel) */
4773       element = EL_STEEL_CHAR_3;
4774       break;
4775
4776     case 0x1636:        /* (blue steel) */
4777       element = EL_STEEL_CHAR_4;
4778       break;
4779
4780     case 0x1637:        /* (blue steel) */
4781       element = EL_STEEL_CHAR_5;
4782       break;
4783
4784     case 0x1638:        /* (blue steel) */
4785       element = EL_STEEL_CHAR_6;
4786       break;
4787
4788     case 0x1639:        /* (blue steel) */
4789       element = EL_STEEL_CHAR_7;
4790       break;
4791
4792     case 0x163a:        /* (blue steel) */
4793       element = EL_STEEL_CHAR_8;
4794       break;
4795
4796     case 0x163b:        /* (blue steel) */
4797       element = EL_STEEL_CHAR_9;
4798       break;
4799
4800     case 0x163c:        /* (blue steel) */
4801       element = EL_STEEL_CHAR_PERIOD;
4802       break;
4803
4804     case 0x163d:        /* (blue steel) */
4805       element = EL_STEEL_CHAR_EXCLAM;
4806       break;
4807
4808     case 0x163e:        /* (blue steel) */
4809       element = EL_STEEL_CHAR_COLON;
4810       break;
4811
4812     case 0x163f:        /* (blue steel) */
4813       element = EL_STEEL_CHAR_LESS;
4814       break;
4815
4816     case 0x1640:        /* (blue steel) */
4817       element = EL_STEEL_CHAR_GREATER;
4818       break;
4819
4820     case 0x1641:        /* (blue steel) */
4821       element = EL_STEEL_CHAR_QUESTION;
4822       break;
4823
4824     case 0x1642:        /* (blue steel) */
4825       element = EL_STEEL_CHAR_COPYRIGHT;
4826       break;
4827
4828     case 0x1643:        /* (blue steel) */
4829       element = EL_STEEL_CHAR_UP;
4830       break;
4831
4832     case 0x1644:        /* (blue steel) */
4833       element = EL_STEEL_CHAR_DOWN;
4834       break;
4835
4836     case 0x1645:        /* (blue steel) */
4837       element = EL_STEEL_CHAR_BUTTON;
4838       break;
4839
4840     case 0x1646:        /* (blue steel) */
4841       element = EL_STEEL_CHAR_PLUS;
4842       break;
4843
4844     case 0x1647:        /* (blue steel) */
4845       element = EL_STEEL_CHAR_MINUS;
4846       break;
4847
4848     case 0x1648:        /* (blue steel) */
4849       element = EL_STEEL_CHAR_APOSTROPHE;
4850       break;
4851
4852     case 0x1649:        /* (blue steel) */
4853       element = EL_STEEL_CHAR_PARENLEFT;
4854       break;
4855
4856     case 0x164a:        /* (blue steel) */
4857       element = EL_STEEL_CHAR_PARENRIGHT;
4858       break;
4859
4860     case 0x164b:        /* (green steel) */
4861       element = EL_STEEL_CHAR_A;
4862       break;
4863
4864     case 0x164c:        /* (green steel) */
4865       element = EL_STEEL_CHAR_B;
4866       break;
4867
4868     case 0x164d:        /* (green steel) */
4869       element = EL_STEEL_CHAR_C;
4870       break;
4871
4872     case 0x164e:        /* (green steel) */
4873       element = EL_STEEL_CHAR_D;
4874       break;
4875
4876     case 0x164f:        /* (green steel) */
4877       element = EL_STEEL_CHAR_E;
4878       break;
4879
4880     case 0x1650:        /* (green steel) */
4881       element = EL_STEEL_CHAR_F;
4882       break;
4883
4884     case 0x1651:        /* (green steel) */
4885       element = EL_STEEL_CHAR_G;
4886       break;
4887
4888     case 0x1652:        /* (green steel) */
4889       element = EL_STEEL_CHAR_H;
4890       break;
4891
4892     case 0x1653:        /* (green steel) */
4893       element = EL_STEEL_CHAR_I;
4894       break;
4895
4896     case 0x1654:        /* (green steel) */
4897       element = EL_STEEL_CHAR_J;
4898       break;
4899
4900     case 0x1655:        /* (green steel) */
4901       element = EL_STEEL_CHAR_K;
4902       break;
4903
4904     case 0x1656:        /* (green steel) */
4905       element = EL_STEEL_CHAR_L;
4906       break;
4907
4908     case 0x1657:        /* (green steel) */
4909       element = EL_STEEL_CHAR_M;
4910       break;
4911
4912     case 0x1658:        /* (green steel) */
4913       element = EL_STEEL_CHAR_N;
4914       break;
4915
4916     case 0x1659:        /* (green steel) */
4917       element = EL_STEEL_CHAR_O;
4918       break;
4919
4920     case 0x165a:        /* (green steel) */
4921       element = EL_STEEL_CHAR_P;
4922       break;
4923
4924     case 0x165b:        /* (green steel) */
4925       element = EL_STEEL_CHAR_Q;
4926       break;
4927
4928     case 0x165c:        /* (green steel) */
4929       element = EL_STEEL_CHAR_R;
4930       break;
4931
4932     case 0x165d:        /* (green steel) */
4933       element = EL_STEEL_CHAR_S;
4934       break;
4935
4936     case 0x165e:        /* (green steel) */
4937       element = EL_STEEL_CHAR_T;
4938       break;
4939
4940     case 0x165f:        /* (green steel) */
4941       element = EL_STEEL_CHAR_U;
4942       break;
4943
4944     case 0x1660:        /* (green steel) */
4945       element = EL_STEEL_CHAR_V;
4946       break;
4947
4948     case 0x1661:        /* (green steel) */
4949       element = EL_STEEL_CHAR_W;
4950       break;
4951
4952     case 0x1662:        /* (green steel) */
4953       element = EL_STEEL_CHAR_X;
4954       break;
4955
4956     case 0x1663:        /* (green steel) */
4957       element = EL_STEEL_CHAR_Y;
4958       break;
4959
4960     case 0x1664:        /* (green steel) */
4961       element = EL_STEEL_CHAR_Z;
4962       break;
4963
4964     case 0x1665:        /* (green steel) */
4965       element = EL_STEEL_CHAR_AUMLAUT;
4966       break;
4967
4968     case 0x1666:        /* (green steel) */
4969       element = EL_STEEL_CHAR_OUMLAUT;
4970       break;
4971
4972     case 0x1667:        /* (green steel) */
4973       element = EL_STEEL_CHAR_UUMLAUT;
4974       break;
4975
4976     case 0x1668:        /* (green steel) */
4977       element = EL_STEEL_CHAR_0;
4978       break;
4979
4980     case 0x1669:        /* (green steel) */
4981       element = EL_STEEL_CHAR_1;
4982       break;
4983
4984     case 0x166a:        /* (green steel) */
4985       element = EL_STEEL_CHAR_2;
4986       break;
4987
4988     case 0x166b:        /* (green steel) */
4989       element = EL_STEEL_CHAR_3;
4990       break;
4991
4992     case 0x166c:        /* (green steel) */
4993       element = EL_STEEL_CHAR_4;
4994       break;
4995
4996     case 0x166d:        /* (green steel) */
4997       element = EL_STEEL_CHAR_5;
4998       break;
4999
5000     case 0x166e:        /* (green steel) */
5001       element = EL_STEEL_CHAR_6;
5002       break;
5003
5004     case 0x166f:        /* (green steel) */
5005       element = EL_STEEL_CHAR_7;
5006       break;
5007
5008     case 0x1670:        /* (green steel) */
5009       element = EL_STEEL_CHAR_8;
5010       break;
5011
5012     case 0x1671:        /* (green steel) */
5013       element = EL_STEEL_CHAR_9;
5014       break;
5015
5016     case 0x1672:        /* (green steel) */
5017       element = EL_STEEL_CHAR_PERIOD;
5018       break;
5019
5020     case 0x1673:        /* (green steel) */
5021       element = EL_STEEL_CHAR_EXCLAM;
5022       break;
5023
5024     case 0x1674:        /* (green steel) */
5025       element = EL_STEEL_CHAR_COLON;
5026       break;
5027
5028     case 0x1675:        /* (green steel) */
5029       element = EL_STEEL_CHAR_LESS;
5030       break;
5031
5032     case 0x1676:        /* (green steel) */
5033       element = EL_STEEL_CHAR_GREATER;
5034       break;
5035
5036     case 0x1677:        /* (green steel) */
5037       element = EL_STEEL_CHAR_QUESTION;
5038       break;
5039
5040     case 0x1678:        /* (green steel) */
5041       element = EL_STEEL_CHAR_COPYRIGHT;
5042       break;
5043
5044     case 0x1679:        /* (green steel) */
5045       element = EL_STEEL_CHAR_UP;
5046       break;
5047
5048     case 0x167a:        /* (green steel) */
5049       element = EL_STEEL_CHAR_DOWN;
5050       break;
5051
5052     case 0x167b:        /* (green steel) */
5053       element = EL_STEEL_CHAR_BUTTON;
5054       break;
5055
5056     case 0x167c:        /* (green steel) */
5057       element = EL_STEEL_CHAR_PLUS;
5058       break;
5059
5060     case 0x167d:        /* (green steel) */
5061       element = EL_STEEL_CHAR_MINUS;
5062       break;
5063
5064     case 0x167e:        /* (green steel) */
5065       element = EL_STEEL_CHAR_APOSTROPHE;
5066       break;
5067
5068     case 0x167f:        /* (green steel) */
5069       element = EL_STEEL_CHAR_PARENLEFT;
5070       break;
5071
5072     case 0x1680:        /* (green steel) */
5073       element = EL_STEEL_CHAR_PARENRIGHT;
5074       break;
5075
5076     case 0x1681:        /* gate (red) */
5077       element = EL_EM_GATE_1;
5078       break;
5079
5080     case 0x1682:        /* secret gate (red) */
5081       element = EL_GATE_1_GRAY;
5082       break;
5083
5084     case 0x1683:        /* gate (yellow) */
5085       element = EL_EM_GATE_2;
5086       break;
5087
5088     case 0x1684:        /* secret gate (yellow) */
5089       element = EL_GATE_2_GRAY;
5090       break;
5091
5092     case 0x1685:        /* gate (blue) */
5093       element = EL_EM_GATE_4;
5094       break;
5095
5096     case 0x1686:        /* secret gate (blue) */
5097       element = EL_GATE_4_GRAY;
5098       break;
5099
5100     case 0x1687:        /* gate (green) */
5101       element = EL_EM_GATE_3;
5102       break;
5103
5104     case 0x1688:        /* secret gate (green) */
5105       element = EL_GATE_3_GRAY;
5106       break;
5107
5108     case 0x1689:        /* gate (white) */
5109       element = EL_DC_GATE_WHITE;
5110       break;
5111
5112     case 0x168a:        /* secret gate (white) */
5113       element = EL_DC_GATE_WHITE_GRAY;
5114       break;
5115
5116     case 0x168b:        /* secret gate (no key) */
5117       element = EL_DC_GATE_FAKE_GRAY;
5118       break;
5119
5120     case 0x168c:
5121       element = EL_ROBOT_WHEEL;
5122       break;
5123
5124     case 0x168d:
5125       element = EL_DC_TIMEGATE_SWITCH;
5126       break;
5127
5128     case 0x168e:
5129       element = EL_ACID_POOL_BOTTOM;
5130       break;
5131
5132     case 0x168f:
5133       element = EL_ACID_POOL_TOPLEFT;
5134       break;
5135
5136     case 0x1690:
5137       element = EL_ACID_POOL_TOPRIGHT;
5138       break;
5139
5140     case 0x1691:
5141       element = EL_ACID_POOL_BOTTOMLEFT;
5142       break;
5143
5144     case 0x1692:
5145       element = EL_ACID_POOL_BOTTOMRIGHT;
5146       break;
5147
5148     case 0x1693:
5149       element = EL_STEELWALL;
5150       break;
5151
5152     case 0x1694:
5153       element = EL_STEELWALL_SLIPPERY;
5154       break;
5155
5156     case 0x1695:        /* steel wall (not round) */
5157       element = EL_STEELWALL;
5158       break;
5159
5160     case 0x1696:        /* steel wall (left) */
5161       element = EL_DC_STEELWALL_1_LEFT;
5162       break;
5163
5164     case 0x1697:        /* steel wall (bottom) */
5165       element = EL_DC_STEELWALL_1_BOTTOM;
5166       break;
5167
5168     case 0x1698:        /* steel wall (right) */
5169       element = EL_DC_STEELWALL_1_RIGHT;
5170       break;
5171
5172     case 0x1699:        /* steel wall (top) */
5173       element = EL_DC_STEELWALL_1_TOP;
5174       break;
5175
5176     case 0x169a:        /* steel wall (left/bottom) */
5177       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5178       break;
5179
5180     case 0x169b:        /* steel wall (right/bottom) */
5181       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5182       break;
5183
5184     case 0x169c:        /* steel wall (right/top) */
5185       element = EL_DC_STEELWALL_1_TOPRIGHT;
5186       break;
5187
5188     case 0x169d:        /* steel wall (left/top) */
5189       element = EL_DC_STEELWALL_1_TOPLEFT;
5190       break;
5191
5192     case 0x169e:        /* steel wall (right/bottom small) */
5193       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5194       break;
5195
5196     case 0x169f:        /* steel wall (left/bottom small) */
5197       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5198       break;
5199
5200     case 0x16a0:        /* steel wall (right/top small) */
5201       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5202       break;
5203
5204     case 0x16a1:        /* steel wall (left/top small) */
5205       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5206       break;
5207
5208     case 0x16a2:        /* steel wall (left/right) */
5209       element = EL_DC_STEELWALL_1_VERTICAL;
5210       break;
5211
5212     case 0x16a3:        /* steel wall (top/bottom) */
5213       element = EL_DC_STEELWALL_1_HORIZONTAL;
5214       break;
5215
5216     case 0x16a4:        /* steel wall 2 (left end) */
5217       element = EL_DC_STEELWALL_2_LEFT;
5218       break;
5219
5220     case 0x16a5:        /* steel wall 2 (right end) */
5221       element = EL_DC_STEELWALL_2_RIGHT;
5222       break;
5223
5224     case 0x16a6:        /* steel wall 2 (top end) */
5225       element = EL_DC_STEELWALL_2_TOP;
5226       break;
5227
5228     case 0x16a7:        /* steel wall 2 (bottom end) */
5229       element = EL_DC_STEELWALL_2_BOTTOM;
5230       break;
5231
5232     case 0x16a8:        /* steel wall 2 (left/right) */
5233       element = EL_DC_STEELWALL_2_HORIZONTAL;
5234       break;
5235
5236     case 0x16a9:        /* steel wall 2 (up/down) */
5237       element = EL_DC_STEELWALL_2_VERTICAL;
5238       break;
5239
5240     case 0x16aa:        /* steel wall 2 (mid) */
5241       element = EL_DC_STEELWALL_2_MIDDLE;
5242       break;
5243
5244     case 0x16ab:
5245       element = EL_SIGN_EXCLAMATION;
5246       break;
5247
5248     case 0x16ac:
5249       element = EL_SIGN_RADIOACTIVITY;
5250       break;
5251
5252     case 0x16ad:
5253       element = EL_SIGN_STOP;
5254       break;
5255
5256     case 0x16ae:
5257       element = EL_SIGN_WHEELCHAIR;
5258       break;
5259
5260     case 0x16af:
5261       element = EL_SIGN_PARKING;
5262       break;
5263
5264     case 0x16b0:
5265       element = EL_SIGN_NO_ENTRY;
5266       break;
5267
5268     case 0x16b1:
5269       element = EL_SIGN_HEART;
5270       break;
5271
5272     case 0x16b2:
5273       element = EL_SIGN_GIVE_WAY;
5274       break;
5275
5276     case 0x16b3:
5277       element = EL_SIGN_ENTRY_FORBIDDEN;
5278       break;
5279
5280     case 0x16b4:
5281       element = EL_SIGN_EMERGENCY_EXIT;
5282       break;
5283
5284     case 0x16b5:
5285       element = EL_SIGN_YIN_YANG;
5286       break;
5287
5288     case 0x16b6:
5289       element = EL_WALL_EMERALD;
5290       break;
5291
5292     case 0x16b7:
5293       element = EL_WALL_DIAMOND;
5294       break;
5295
5296     case 0x16b8:
5297       element = EL_WALL_PEARL;
5298       break;
5299
5300     case 0x16b9:
5301       element = EL_WALL_CRYSTAL;
5302       break;
5303
5304     case 0x16ba:
5305       element = EL_INVISIBLE_WALL;
5306       break;
5307
5308     case 0x16bb:
5309       element = EL_INVISIBLE_STEELWALL;
5310       break;
5311
5312       /* 0x16bc - 0x16cb: */
5313       /* EL_INVISIBLE_SAND */
5314
5315     case 0x16cc:
5316       element = EL_LIGHT_SWITCH;
5317       break;
5318
5319     case 0x16cd:
5320       element = EL_ENVELOPE_1;
5321       break;
5322
5323     default:
5324       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5325         element = EL_DIAMOND;
5326       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
5327         element = EL_EMERALD;
5328       else if (element >= 0x157c && element <= 0x158b)
5329         element = EL_SAND;
5330       else if (element >= 0x1590 && element <= 0x159f)
5331         element = EL_DC_LANDMINE;
5332       else if (element >= 0x16bc && element <= 0x16cb)
5333         element = EL_INVISIBLE_SAND;
5334       else
5335       {
5336         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5337         element = EL_UNKNOWN;
5338       }
5339       break;
5340   }
5341
5342   return getMappedElement(element);
5343 }
5344
5345 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5346                                        int nr)
5347 {
5348   byte header[DC_LEVEL_HEADER_SIZE];
5349   int envelope_size;
5350   int envelope_header_pos = 62;
5351   int envelope_content_pos = 94;
5352   int level_name_pos = 251;
5353   int level_author_pos = 292;
5354   int envelope_header_len;
5355   int envelope_content_len;
5356   int level_name_len;
5357   int level_author_len;
5358   int fieldx, fieldy;
5359   int num_yamyam_contents;
5360   int i, x, y;
5361
5362   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
5363
5364   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5365   {
5366     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5367
5368     header[i * 2 + 0] = header_word >> 8;
5369     header[i * 2 + 1] = header_word & 0xff;
5370   }
5371
5372   /* read some values from level header to check level decoding integrity */
5373   fieldx = header[6] | (header[7] << 8);
5374   fieldy = header[8] | (header[9] << 8);
5375   num_yamyam_contents = header[60] | (header[61] << 8);
5376
5377   /* do some simple sanity checks to ensure that level was correctly decoded */
5378   if (fieldx < 1 || fieldx > 256 ||
5379       fieldy < 1 || fieldy > 256 ||
5380       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5381   {
5382     level->no_valid_file = TRUE;
5383
5384     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5385
5386     return;
5387   }
5388
5389   /* maximum envelope header size is 31 bytes */
5390   envelope_header_len   = header[envelope_header_pos];
5391   /* maximum envelope content size is 110 (156?) bytes */
5392   envelope_content_len  = header[envelope_content_pos];
5393
5394   /* maximum level title size is 40 bytes */
5395   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5396   /* maximum level author size is 30 (51?) bytes */
5397   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5398
5399   envelope_size = 0;
5400
5401   for (i = 0; i < envelope_header_len; i++)
5402     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5403       level->envelope[0].text[envelope_size++] =
5404         header[envelope_header_pos + 1 + i];
5405
5406   if (envelope_header_len > 0 && envelope_content_len > 0)
5407   {
5408     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5409       level->envelope[0].text[envelope_size++] = '\n';
5410     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5411       level->envelope[0].text[envelope_size++] = '\n';
5412   }
5413
5414   for (i = 0; i < envelope_content_len; i++)
5415     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5416       level->envelope[0].text[envelope_size++] =
5417         header[envelope_content_pos + 1 + i];
5418
5419   level->envelope[0].text[envelope_size] = '\0';
5420
5421   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5422   level->envelope[0].ysize = 10;
5423   level->envelope[0].autowrap = TRUE;
5424   level->envelope[0].centered = TRUE;
5425
5426   for (i = 0; i < level_name_len; i++)
5427     level->name[i] = header[level_name_pos + 1 + i];
5428   level->name[level_name_len] = '\0';
5429
5430   for (i = 0; i < level_author_len; i++)
5431     level->author[i] = header[level_author_pos + 1 + i];
5432   level->author[level_author_len] = '\0';
5433
5434   num_yamyam_contents = header[60] | (header[61] << 8);
5435   level->num_yamyam_contents =
5436     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5437
5438   for (i = 0; i < num_yamyam_contents; i++)
5439   {
5440     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5441     {
5442       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5443       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5444
5445       if (i < MAX_ELEMENT_CONTENTS)
5446         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5447     }
5448   }
5449
5450   fieldx = header[6] | (header[7] << 8);
5451   fieldy = header[8] | (header[9] << 8);
5452   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5453   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5454
5455   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5456   {
5457     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5458     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5459
5460     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5461       level->field[x][y] = getMappedElement_DC(element_dc);
5462   }
5463
5464   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5465   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5466   level->field[x][y] = EL_PLAYER_1;
5467
5468   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5469   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5470   level->field[x][y] = EL_PLAYER_2;
5471
5472   level->gems_needed            = header[18] | (header[19] << 8);
5473
5474   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5475   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5476   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5477   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5478   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5479   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5480   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5481   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5482   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5483   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5484   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5485   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5486
5487   level->time                   = header[44] | (header[45] << 8);
5488
5489   level->amoeba_speed           = header[46] | (header[47] << 8);
5490   level->time_light             = header[48] | (header[49] << 8);
5491   level->time_timegate          = header[50] | (header[51] << 8);
5492   level->time_wheel             = header[52] | (header[53] << 8);
5493   level->time_magic_wall        = header[54] | (header[55] << 8);
5494   level->extra_time             = header[56] | (header[57] << 8);
5495   level->shield_normal_time     = header[58] | (header[59] << 8);
5496
5497   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5498      can slip down from flat walls, like normal walls and steel walls */
5499   level->em_slippery_gems = TRUE;
5500 }
5501
5502 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5503                                      struct LevelFileInfo *level_file_info,
5504                                      boolean level_info_only)
5505 {
5506   char *filename = level_file_info->filename;
5507   File *file;
5508   int num_magic_bytes = 8;
5509   char magic_bytes[num_magic_bytes + 1];
5510   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5511
5512   if (!(file = openFile(filename, MODE_READ)))
5513   {
5514     level->no_valid_file = TRUE;
5515
5516     if (!level_info_only)
5517       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5518
5519     return;
5520   }
5521
5522   // fseek(file, 0x0000, SEEK_SET);
5523
5524   if (level_file_info->packed)
5525   {
5526     /* read "magic bytes" from start of file */
5527     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5528       magic_bytes[0] = '\0';
5529
5530     /* check "magic bytes" for correct file format */
5531     if (!strPrefix(magic_bytes, "DC2"))
5532     {
5533       level->no_valid_file = TRUE;
5534
5535       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5536             filename);
5537
5538       return;
5539     }
5540
5541     if (strPrefix(magic_bytes, "DC2Win95") ||
5542         strPrefix(magic_bytes, "DC2Win98"))
5543     {
5544       int position_first_level = 0x00fa;
5545       int extra_bytes = 4;
5546       int skip_bytes;
5547
5548       /* advance file stream to first level inside the level package */
5549       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5550
5551       /* each block of level data is followed by block of non-level data */
5552       num_levels_to_skip *= 2;
5553
5554       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5555       while (num_levels_to_skip >= 0)
5556       {
5557         /* advance file stream to next level inside the level package */
5558         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5559         {
5560           level->no_valid_file = TRUE;
5561
5562           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5563                 filename);
5564
5565           return;
5566         }
5567
5568         /* skip apparently unused extra bytes following each level */
5569         ReadUnusedBytesFromFile(file, extra_bytes);
5570
5571         /* read size of next level in level package */
5572         skip_bytes = getFile32BitLE(file);
5573
5574         num_levels_to_skip--;
5575       }
5576     }
5577     else
5578     {
5579       level->no_valid_file = TRUE;
5580
5581       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5582             filename);
5583
5584       return;
5585     }
5586   }
5587
5588   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5589
5590   closeFile(file);
5591 }
5592
5593
5594 /* ------------------------------------------------------------------------- */
5595 /* functions for loading SB level                                            */
5596 /* ------------------------------------------------------------------------- */
5597
5598 int getMappedElement_SB(int element_ascii, boolean use_ces)
5599 {
5600   static struct
5601   {
5602     int ascii;
5603     int sb;
5604     int ce;
5605   }
5606   sb_element_mapping[] =
5607   {
5608     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
5609     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
5610     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
5611     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
5612     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
5613     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
5614     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
5615     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
5616
5617     { 0,   -1,                      -1          },
5618   };
5619
5620   int i;
5621
5622   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5623     if (element_ascii == sb_element_mapping[i].ascii)
5624       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5625
5626   return EL_UNDEFINED;
5627 }
5628
5629 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5630                                      struct LevelFileInfo *level_file_info,
5631                                      boolean level_info_only)
5632 {
5633   char *filename = level_file_info->filename;
5634   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5635   char last_comment[MAX_LINE_LEN];
5636   char level_name[MAX_LINE_LEN];
5637   char *line_ptr;
5638   File *file;
5639   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5640   boolean read_continued_line = FALSE;
5641   boolean reading_playfield = FALSE;
5642   boolean got_valid_playfield_line = FALSE;
5643   boolean invalid_playfield_char = FALSE;
5644   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5645   int file_level_nr = 0;
5646   int line_nr = 0;
5647   int x = 0, y = 0;             /* initialized to make compilers happy */
5648
5649   last_comment[0] = '\0';
5650   level_name[0] = '\0';
5651
5652   if (!(file = openFile(filename, MODE_READ)))
5653   {
5654     level->no_valid_file = TRUE;
5655
5656     if (!level_info_only)
5657       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5658
5659     return;
5660   }
5661
5662   while (!checkEndOfFile(file))
5663   {
5664     /* level successfully read, but next level may follow here */
5665     if (!got_valid_playfield_line && reading_playfield)
5666     {
5667       /* read playfield from single level file -- skip remaining file */
5668       if (!level_file_info->packed)
5669         break;
5670
5671       if (file_level_nr >= num_levels_to_skip)
5672         break;
5673
5674       file_level_nr++;
5675
5676       last_comment[0] = '\0';
5677       level_name[0] = '\0';
5678
5679       reading_playfield = FALSE;
5680     }
5681
5682     got_valid_playfield_line = FALSE;
5683
5684     /* read next line of input file */
5685     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5686       break;
5687
5688     /* check if line was completely read and is terminated by line break */
5689     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5690       line_nr++;
5691
5692     /* cut trailing line break (this can be newline and/or carriage return) */
5693     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5694       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5695         *line_ptr = '\0';
5696
5697     /* copy raw input line for later use (mainly debugging output) */
5698     strcpy(line_raw, line);
5699
5700     if (read_continued_line)
5701     {
5702       /* append new line to existing line, if there is enough space */
5703       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5704         strcat(previous_line, line_ptr);
5705
5706       strcpy(line, previous_line);      /* copy storage buffer to line */
5707
5708       read_continued_line = FALSE;
5709     }
5710
5711     /* if the last character is '\', continue at next line */
5712     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5713     {
5714       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
5715       strcpy(previous_line, line);      /* copy line to storage buffer */
5716
5717       read_continued_line = TRUE;
5718
5719       continue;
5720     }
5721
5722     /* skip empty lines */
5723     if (line[0] == '\0')
5724       continue;
5725
5726     /* extract comment text from comment line */
5727     if (line[0] == ';')
5728     {
5729       for (line_ptr = line; *line_ptr; line_ptr++)
5730         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5731           break;
5732
5733       strcpy(last_comment, line_ptr);
5734
5735       continue;
5736     }
5737
5738     /* extract level title text from line containing level title */
5739     if (line[0] == '\'')
5740     {
5741       strcpy(level_name, &line[1]);
5742
5743       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5744         level_name[strlen(level_name) - 1] = '\0';
5745
5746       continue;
5747     }
5748
5749     /* skip lines containing only spaces (or empty lines) */
5750     for (line_ptr = line; *line_ptr; line_ptr++)
5751       if (*line_ptr != ' ')
5752         break;
5753     if (*line_ptr == '\0')
5754       continue;
5755
5756     /* at this point, we have found a line containing part of a playfield */
5757
5758     got_valid_playfield_line = TRUE;
5759
5760     if (!reading_playfield)
5761     {
5762       reading_playfield = TRUE;
5763       invalid_playfield_char = FALSE;
5764
5765       for (x = 0; x < MAX_LEV_FIELDX; x++)
5766         for (y = 0; y < MAX_LEV_FIELDY; y++)
5767           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5768
5769       level->fieldx = 0;
5770       level->fieldy = 0;
5771
5772       /* start with topmost tile row */
5773       y = 0;
5774     }
5775
5776     /* skip playfield line if larger row than allowed */
5777     if (y >= MAX_LEV_FIELDY)
5778       continue;
5779
5780     /* start with leftmost tile column */
5781     x = 0;
5782
5783     /* read playfield elements from line */
5784     for (line_ptr = line; *line_ptr; line_ptr++)
5785     {
5786       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5787
5788       /* stop parsing playfield line if larger column than allowed */
5789       if (x >= MAX_LEV_FIELDX)
5790         break;
5791
5792       if (mapped_sb_element == EL_UNDEFINED)
5793       {
5794         invalid_playfield_char = TRUE;
5795
5796         break;
5797       }
5798
5799       level->field[x][y] = mapped_sb_element;
5800
5801       /* continue with next tile column */
5802       x++;
5803
5804       level->fieldx = MAX(x, level->fieldx);
5805     }
5806
5807     if (invalid_playfield_char)
5808     {
5809       /* if first playfield line, treat invalid lines as comment lines */
5810       if (y == 0)
5811         reading_playfield = FALSE;
5812
5813       continue;
5814     }
5815
5816     /* continue with next tile row */
5817     y++;
5818   }
5819
5820   closeFile(file);
5821
5822   level->fieldy = y;
5823
5824   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5825   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5826
5827   if (!reading_playfield)
5828   {
5829     level->no_valid_file = TRUE;
5830
5831     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5832
5833     return;
5834   }
5835
5836   if (*level_name != '\0')
5837   {
5838     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5839     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5840   }
5841   else if (*last_comment != '\0')
5842   {
5843     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5844     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5845   }
5846   else
5847   {
5848     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5849   }
5850
5851   /* set all empty fields beyond the border walls to invisible steel wall */
5852   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5853   {
5854     if ((x == 0 || x == level->fieldx - 1 ||
5855          y == 0 || y == level->fieldy - 1) &&
5856         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5857       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5858                      level->field, level->fieldx, level->fieldy);
5859   }
5860
5861   /* set special level settings for Sokoban levels */
5862
5863   level->time = 0;
5864   level->use_step_counter = TRUE;
5865
5866   if (load_xsb_to_ces)
5867   {
5868     /* special global settings can now be set in level template */
5869
5870     level->use_custom_template = TRUE;
5871   }
5872 }
5873
5874
5875 /* ------------------------------------------------------------------------- */
5876 /* functions for handling native levels                                      */
5877 /* ------------------------------------------------------------------------- */
5878
5879 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5880                                      struct LevelFileInfo *level_file_info,
5881                                      boolean level_info_only)
5882 {
5883   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5884     level->no_valid_file = TRUE;
5885 }
5886
5887 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5888                                      struct LevelFileInfo *level_file_info,
5889                                      boolean level_info_only)
5890 {
5891   int pos = 0;
5892
5893   /* determine position of requested level inside level package */
5894   if (level_file_info->packed)
5895     pos = level_file_info->nr - leveldir_current->first_level;
5896
5897   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5898     level->no_valid_file = TRUE;
5899 }
5900
5901 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5902 {
5903   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5904     CopyNativeLevel_RND_to_EM(level);
5905   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5906     CopyNativeLevel_RND_to_SP(level);
5907 }
5908
5909 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5910 {
5911   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5912     CopyNativeLevel_EM_to_RND(level);
5913   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5914     CopyNativeLevel_SP_to_RND(level);
5915 }
5916
5917 void SaveNativeLevel(struct LevelInfo *level)
5918 {
5919   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5920   {
5921     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5922     char *filename = getLevelFilenameFromBasename(basename);
5923
5924     CopyNativeLevel_RND_to_SP(level);
5925     CopyNativeTape_RND_to_SP(level);
5926
5927     SaveNativeLevel_SP(filename);
5928   }
5929 }
5930
5931
5932 /* ------------------------------------------------------------------------- */
5933 /* functions for loading generic level                                       */
5934 /* ------------------------------------------------------------------------- */
5935
5936 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5937                                   struct LevelFileInfo *level_file_info,
5938                                   boolean level_info_only)
5939 {
5940   /* always start with reliable default values */
5941   setLevelInfoToDefaults(level, level_info_only, TRUE);
5942
5943   switch (level_file_info->type)
5944   {
5945     case LEVEL_FILE_TYPE_RND:
5946       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5947       break;
5948
5949     case LEVEL_FILE_TYPE_EM:
5950       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5951       level->game_engine_type = GAME_ENGINE_TYPE_EM;
5952       break;
5953
5954     case LEVEL_FILE_TYPE_SP:
5955       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5956       level->game_engine_type = GAME_ENGINE_TYPE_SP;
5957       break;
5958
5959     case LEVEL_FILE_TYPE_DC:
5960       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5961       break;
5962
5963     case LEVEL_FILE_TYPE_SB:
5964       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5965       break;
5966
5967     default:
5968       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5969       break;
5970   }
5971
5972   /* if level file is invalid, restore level structure to default values */
5973   if (level->no_valid_file)
5974     setLevelInfoToDefaults(level, level_info_only, FALSE);
5975
5976   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5977     level->game_engine_type = GAME_ENGINE_TYPE_RND;
5978
5979   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5980     CopyNativeLevel_Native_to_RND(level);
5981 }
5982
5983 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5984 {
5985   static struct LevelFileInfo level_file_info;
5986
5987   /* always start with reliable default values */
5988   setFileInfoToDefaults(&level_file_info);
5989
5990   level_file_info.nr = 0;                       /* unknown level number */
5991   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
5992   level_file_info.filename = filename;
5993
5994   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5995 }
5996
5997 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5998 {
5999   int i, j;
6000
6001   if (leveldir_current == NULL)         /* only when dumping level */
6002     return;
6003
6004   /* all engine modifications also valid for levels which use latest engine */
6005   if (level->game_version < VERSION_IDENT(3,2,0,5))
6006   {
6007     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
6008     level->score[SC_TIME_BONUS] /= 10;
6009   }
6010
6011   if (leveldir_current->latest_engine)
6012   {
6013     /* ---------- use latest game engine ----------------------------------- */
6014
6015     /* For all levels which are forced to use the latest game engine version
6016        (normally all but user contributed, private and undefined levels), set
6017        the game engine version to the actual version; this allows for actual
6018        corrections in the game engine to take effect for existing, converted
6019        levels (from "classic" or other existing games) to make the emulation
6020        of the corresponding game more accurate, while (hopefully) not breaking
6021        existing levels created from other players. */
6022
6023     level->game_version = GAME_VERSION_ACTUAL;
6024
6025     /* Set special EM style gems behaviour: EM style gems slip down from
6026        normal, steel and growing wall. As this is a more fundamental change,
6027        it seems better to set the default behaviour to "off" (as it is more
6028        natural) and make it configurable in the level editor (as a property
6029        of gem style elements). Already existing converted levels (neither
6030        private nor contributed levels) are changed to the new behaviour. */
6031
6032     if (level->file_version < FILE_VERSION_2_0)
6033       level->em_slippery_gems = TRUE;
6034
6035     return;
6036   }
6037
6038   /* ---------- use game engine the level was created with ----------------- */
6039
6040   /* For all levels which are not forced to use the latest game engine
6041      version (normally user contributed, private and undefined levels),
6042      use the version of the game engine the levels were created for.
6043
6044      Since 2.0.1, the game engine version is now directly stored
6045      in the level file (chunk "VERS"), so there is no need anymore
6046      to set the game version from the file version (except for old,
6047      pre-2.0 levels, where the game version is still taken from the
6048      file format version used to store the level -- see above). */
6049
6050   /* player was faster than enemies in 1.0.0 and before */
6051   if (level->file_version == FILE_VERSION_1_0)
6052     for (i = 0; i < MAX_PLAYERS; i++)
6053       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6054
6055   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6056   if (level->game_version == VERSION_IDENT(2,0,1,0))
6057     level->em_slippery_gems = TRUE;
6058
6059   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6060   if (level->game_version < VERSION_IDENT(2,2,0,0))
6061     level->use_spring_bug = TRUE;
6062
6063   if (level->game_version < VERSION_IDENT(3,2,0,5))
6064   {
6065     /* time orb caused limited time in endless time levels before 3.2.0-5 */
6066     level->use_time_orb_bug = TRUE;
6067
6068     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6069     level->block_snap_field = FALSE;
6070
6071     /* extra time score was same value as time left score before 3.2.0-5 */
6072     level->extra_time_score = level->score[SC_TIME_BONUS];
6073   }
6074
6075   if (level->game_version < VERSION_IDENT(3,2,0,7))
6076   {
6077     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6078     level->continuous_snapping = FALSE;
6079   }
6080
6081   /* only few elements were able to actively move into acid before 3.1.0 */
6082   /* trigger settings did not exist before 3.1.0; set to default "any" */
6083   if (level->game_version < VERSION_IDENT(3,1,0,0))
6084   {
6085     /* correct "can move into acid" settings (all zero in old levels) */
6086
6087     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6088     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6089
6090     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6091     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6092     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6093     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6094
6095     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6096       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6097
6098     /* correct trigger settings (stored as zero == "none" in old levels) */
6099
6100     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6101     {
6102       int element = EL_CUSTOM_START + i;
6103       struct ElementInfo *ei = &element_info[element];
6104
6105       for (j = 0; j < ei->num_change_pages; j++)
6106       {
6107         struct ElementChangeInfo *change = &ei->change_page[j];
6108
6109         change->trigger_player = CH_PLAYER_ANY;
6110         change->trigger_page = CH_PAGE_ANY;
6111       }
6112     }
6113   }
6114
6115   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6116   {
6117     int element = EL_CUSTOM_256;
6118     struct ElementInfo *ei = &element_info[element];
6119     struct ElementChangeInfo *change = &ei->change_page[0];
6120
6121     /* This is needed to fix a problem that was caused by a bugfix in function
6122        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6123        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6124        not replace walkable elements, but instead just placed the player on it,
6125        without placing the Sokoban field under the player). Unfortunately, this
6126        breaks "Snake Bite" style levels when the snake is halfway through a door
6127        that just closes (the snake head is still alive and can be moved in this
6128        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6129        player (without Sokoban element) which then gets killed as designed). */
6130
6131     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6132          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6133         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6134       change->target_element = EL_PLAYER_1;
6135   }
6136
6137   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6138   if (level->game_version < VERSION_IDENT(3,2,5,0))
6139   {
6140     /* This is needed to fix a problem that was caused by a bugfix in function
6141        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6142        corrects the behaviour when a custom element changes to another custom
6143        element with a higher element number that has change actions defined.
6144        Normally, only one change per frame is allowed for custom elements.
6145        Therefore, it is checked if a custom element already changed in the
6146        current frame; if it did, subsequent changes are suppressed.
6147        Unfortunately, this is only checked for element changes, but not for
6148        change actions, which are still executed. As the function above loops
6149        through all custom elements from lower to higher, an element change
6150        resulting in a lower CE number won't be checked again, while a target
6151        element with a higher number will also be checked, and potential change
6152        actions will get executed for this CE, too (which is wrong), while
6153        further changes are ignored (which is correct). As this bugfix breaks
6154        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6155        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6156        behaviour for existing levels and tapes that make use of this bug */
6157
6158     level->use_action_after_change_bug = TRUE;
6159   }
6160
6161   /* not centering level after relocating player was default only in 3.2.3 */
6162   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
6163     level->shifted_relocation = TRUE;
6164
6165   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6166   if (level->game_version < VERSION_IDENT(3,2,6,0))
6167     level->em_explodes_by_fire = TRUE;
6168 }
6169
6170 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6171 {
6172   int i, j, x, y;
6173
6174   /* map custom element change events that have changed in newer versions
6175      (these following values were accidentally changed in version 3.0.1)
6176      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6177   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6178   {
6179     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6180     {
6181       int element = EL_CUSTOM_START + i;
6182
6183       /* order of checking and copying events to be mapped is important */
6184       /* (do not change the start and end value -- they are constant) */
6185       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6186       {
6187         if (HAS_CHANGE_EVENT(element, j - 2))
6188         {
6189           SET_CHANGE_EVENT(element, j - 2, FALSE);
6190           SET_CHANGE_EVENT(element, j, TRUE);
6191         }
6192       }
6193
6194       /* order of checking and copying events to be mapped is important */
6195       /* (do not change the start and end value -- they are constant) */
6196       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6197       {
6198         if (HAS_CHANGE_EVENT(element, j - 1))
6199         {
6200           SET_CHANGE_EVENT(element, j - 1, FALSE);
6201           SET_CHANGE_EVENT(element, j, TRUE);
6202         }
6203       }
6204     }
6205   }
6206
6207   /* initialize "can_change" field for old levels with only one change page */
6208   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6209   {
6210     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6211     {
6212       int element = EL_CUSTOM_START + i;
6213
6214       if (CAN_CHANGE(element))
6215         element_info[element].change->can_change = TRUE;
6216     }
6217   }
6218
6219   /* correct custom element values (for old levels without these options) */
6220   if (level->game_version < VERSION_IDENT(3,1,1,0))
6221   {
6222     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6223     {
6224       int element = EL_CUSTOM_START + i;
6225       struct ElementInfo *ei = &element_info[element];
6226
6227       if (ei->access_direction == MV_NO_DIRECTION)
6228         ei->access_direction = MV_ALL_DIRECTIONS;
6229     }
6230   }
6231
6232   /* correct custom element values (fix invalid values for all versions) */
6233   if (1)
6234   {
6235     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6236     {
6237       int element = EL_CUSTOM_START + i;
6238       struct ElementInfo *ei = &element_info[element];
6239
6240       for (j = 0; j < ei->num_change_pages; j++)
6241       {
6242         struct ElementChangeInfo *change = &ei->change_page[j];
6243
6244         if (change->trigger_player == CH_PLAYER_NONE)
6245           change->trigger_player = CH_PLAYER_ANY;
6246
6247         if (change->trigger_side == CH_SIDE_NONE)
6248           change->trigger_side = CH_SIDE_ANY;
6249       }
6250     }
6251   }
6252
6253   /* initialize "can_explode" field for old levels which did not store this */
6254   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6255   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6256   {
6257     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6258     {
6259       int element = EL_CUSTOM_START + i;
6260
6261       if (EXPLODES_1X1_OLD(element))
6262         element_info[element].explosion_type = EXPLODES_1X1;
6263
6264       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6265                                              EXPLODES_SMASHED(element) ||
6266                                              EXPLODES_IMPACT(element)));
6267     }
6268   }
6269
6270   /* correct previously hard-coded move delay values for maze runner style */
6271   if (level->game_version < VERSION_IDENT(3,1,1,0))
6272   {
6273     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6274     {
6275       int element = EL_CUSTOM_START + i;
6276
6277       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6278       {
6279         /* previously hard-coded and therefore ignored */
6280         element_info[element].move_delay_fixed = 9;
6281         element_info[element].move_delay_random = 0;
6282       }
6283     }
6284   }
6285
6286   /* map elements that have changed in newer versions */
6287   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6288                                                     level->game_version);
6289   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6290     for (x = 0; x < 3; x++)
6291       for (y = 0; y < 3; y++)
6292         level->yamyam_content[i].e[x][y] =
6293           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6294                                     level->game_version);
6295
6296   /* initialize element properties for level editor etc. */
6297   InitElementPropertiesEngine(level->game_version);
6298   InitElementPropertiesAfterLoading(level->game_version);
6299   InitElementPropertiesGfxElement();
6300 }
6301
6302 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6303 {
6304   int x, y;
6305
6306   /* map elements that have changed in newer versions */
6307   for (y = 0; y < level->fieldy; y++)
6308     for (x = 0; x < level->fieldx; x++)
6309       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6310                                                      level->game_version);
6311
6312   /* clear unused playfield data (nicer if level gets resized in editor) */
6313   for (x = 0; x < MAX_LEV_FIELDX; x++)
6314     for (y = 0; y < MAX_LEV_FIELDY; y++)
6315       if (x >= level->fieldx || y >= level->fieldy)
6316         level->field[x][y] = EL_EMPTY;
6317
6318   /* copy elements to runtime playfield array */
6319   for (x = 0; x < MAX_LEV_FIELDX; x++)
6320     for (y = 0; y < MAX_LEV_FIELDY; y++)
6321       Feld[x][y] = level->field[x][y];
6322
6323   /* initialize level size variables for faster access */
6324   lev_fieldx = level->fieldx;
6325   lev_fieldy = level->fieldy;
6326
6327   /* determine border element for this level */
6328   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6329     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6330   else
6331     SetBorderElement();
6332 }
6333
6334 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6335 {
6336   struct LevelFileInfo *level_file_info = &level->file_info;
6337
6338   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6339     CopyNativeLevel_RND_to_Native(level);
6340 }
6341
6342 void LoadLevelTemplate(int nr)
6343 {
6344   char *filename;
6345
6346   setLevelFileInfo(&level_template.file_info, nr);
6347   filename = level_template.file_info.filename;
6348
6349   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6350
6351   LoadLevel_InitVersion(&level_template, filename);
6352   LoadLevel_InitElements(&level_template, filename);
6353
6354   ActivateLevelTemplate();
6355 }
6356
6357 void LoadLevel(int nr)
6358 {
6359   char *filename;
6360
6361   setLevelFileInfo(&level.file_info, nr);
6362   filename = level.file_info.filename;
6363
6364   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6365
6366   if (level.use_custom_template)
6367     LoadLevelTemplate(-1);
6368
6369   LoadLevel_InitVersion(&level, filename);
6370   LoadLevel_InitElements(&level, filename);
6371   LoadLevel_InitPlayfield(&level, filename);
6372
6373   LoadLevel_InitNativeEngines(&level, filename);
6374 }
6375
6376 void LoadLevelInfoOnly(int nr)
6377 {
6378   setLevelFileInfo(&level.file_info, nr);
6379
6380   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6381 }
6382
6383 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6384 {
6385   int chunk_size = 0;
6386
6387   chunk_size += putFileVersion(file, level->file_version);
6388   chunk_size += putFileVersion(file, level->game_version);
6389
6390   return chunk_size;
6391 }
6392
6393 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6394 {
6395   int chunk_size = 0;
6396
6397   chunk_size += putFile16BitBE(file, level->creation_date.year);
6398   chunk_size += putFile8Bit(file,    level->creation_date.month);
6399   chunk_size += putFile8Bit(file,    level->creation_date.day);
6400
6401   return chunk_size;
6402 }
6403
6404 #if ENABLE_HISTORIC_CHUNKS
6405 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6406 {
6407   int i, x, y;
6408
6409   putFile8Bit(file, level->fieldx);
6410   putFile8Bit(file, level->fieldy);
6411
6412   putFile16BitBE(file, level->time);
6413   putFile16BitBE(file, level->gems_needed);
6414
6415   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6416     putFile8Bit(file, level->name[i]);
6417
6418   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6419     putFile8Bit(file, level->score[i]);
6420
6421   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6422     for (y = 0; y < 3; y++)
6423       for (x = 0; x < 3; x++)
6424         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6425                            level->yamyam_content[i].e[x][y]));
6426   putFile8Bit(file, level->amoeba_speed);
6427   putFile8Bit(file, level->time_magic_wall);
6428   putFile8Bit(file, level->time_wheel);
6429   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6430                      level->amoeba_content));
6431   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6432   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6433   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6434   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6435
6436   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6437
6438   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6439   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6440   putFile32BitBE(file, level->can_move_into_acid_bits);
6441   putFile8Bit(file, level->dont_collide_with_bits);
6442
6443   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6444   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6445
6446   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6447   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6448   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6449
6450   putFile8Bit(file, level->game_engine_type);
6451
6452   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6453 }
6454 #endif
6455
6456 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6457 {
6458   int chunk_size = 0;
6459   int i;
6460
6461   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6462     chunk_size += putFile8Bit(file, level->name[i]);
6463
6464   return chunk_size;
6465 }
6466
6467 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6468 {
6469   int chunk_size = 0;
6470   int i;
6471
6472   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6473     chunk_size += putFile8Bit(file, level->author[i]);
6474
6475   return chunk_size;
6476 }
6477
6478 #if ENABLE_HISTORIC_CHUNKS
6479 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6480 {
6481   int chunk_size = 0;
6482   int x, y;
6483
6484   for (y = 0; y < level->fieldy; y++) 
6485     for (x = 0; x < level->fieldx; x++) 
6486       if (level->encoding_16bit_field)
6487         chunk_size += putFile16BitBE(file, level->field[x][y]);
6488       else
6489         chunk_size += putFile8Bit(file, level->field[x][y]);
6490
6491   return chunk_size;
6492 }
6493 #endif
6494
6495 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6496 {
6497   int chunk_size = 0;
6498   int x, y;
6499
6500   for (y = 0; y < level->fieldy; y++) 
6501     for (x = 0; x < level->fieldx; x++) 
6502       chunk_size += putFile16BitBE(file, level->field[x][y]);
6503
6504   return chunk_size;
6505 }
6506
6507 #if ENABLE_HISTORIC_CHUNKS
6508 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6509 {
6510   int i, x, y;
6511
6512   putFile8Bit(file, EL_YAMYAM);
6513   putFile8Bit(file, level->num_yamyam_contents);
6514   putFile8Bit(file, 0);
6515   putFile8Bit(file, 0);
6516
6517   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6518     for (y = 0; y < 3; y++)
6519       for (x = 0; x < 3; x++)
6520         if (level->encoding_16bit_field)
6521           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6522         else
6523           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6524 }
6525 #endif
6526
6527 #if ENABLE_HISTORIC_CHUNKS
6528 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6529 {
6530   int i, x, y;
6531   int num_contents, content_xsize, content_ysize;
6532   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6533
6534   if (element == EL_YAMYAM)
6535   {
6536     num_contents = level->num_yamyam_contents;
6537     content_xsize = 3;
6538     content_ysize = 3;
6539
6540     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6541       for (y = 0; y < 3; y++)
6542         for (x = 0; x < 3; x++)
6543           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6544   }
6545   else if (element == EL_BD_AMOEBA)
6546   {
6547     num_contents = 1;
6548     content_xsize = 1;
6549     content_ysize = 1;
6550
6551     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6552       for (y = 0; y < 3; y++)
6553         for (x = 0; x < 3; x++)
6554           content_array[i][x][y] = EL_EMPTY;
6555     content_array[0][0][0] = level->amoeba_content;
6556   }
6557   else
6558   {
6559     /* chunk header already written -- write empty chunk data */
6560     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6561
6562     Error(ERR_WARN, "cannot save content for element '%d'", element);
6563     return;
6564   }
6565
6566   putFile16BitBE(file, element);
6567   putFile8Bit(file, num_contents);
6568   putFile8Bit(file, content_xsize);
6569   putFile8Bit(file, content_ysize);
6570
6571   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6572
6573   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6574     for (y = 0; y < 3; y++)
6575       for (x = 0; x < 3; x++)
6576         putFile16BitBE(file, content_array[i][x][y]);
6577 }
6578 #endif
6579
6580 #if ENABLE_HISTORIC_CHUNKS
6581 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6582 {
6583   int envelope_nr = element - EL_ENVELOPE_1;
6584   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6585   int chunk_size = 0;
6586   int i;
6587
6588   chunk_size += putFile16BitBE(file, element);
6589   chunk_size += putFile16BitBE(file, envelope_len);
6590   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6591   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6592
6593   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6594   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6595
6596   for (i = 0; i < envelope_len; i++)
6597     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6598
6599   return chunk_size;
6600 }
6601 #endif
6602
6603 #if ENABLE_HISTORIC_CHUNKS
6604 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6605                            int num_changed_custom_elements)
6606 {
6607   int i, check = 0;
6608
6609   putFile16BitBE(file, num_changed_custom_elements);
6610
6611   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6612   {
6613     int element = EL_CUSTOM_START + i;
6614
6615     struct ElementInfo *ei = &element_info[element];
6616
6617     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6618     {
6619       if (check < num_changed_custom_elements)
6620       {
6621         putFile16BitBE(file, element);
6622         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6623       }
6624
6625       check++;
6626     }
6627   }
6628
6629   if (check != num_changed_custom_elements)     /* should not happen */
6630     Error(ERR_WARN, "inconsistent number of custom element properties");
6631 }
6632 #endif
6633
6634 #if ENABLE_HISTORIC_CHUNKS
6635 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6636                            int num_changed_custom_elements)
6637 {
6638   int i, check = 0;
6639
6640   putFile16BitBE(file, num_changed_custom_elements);
6641
6642   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6643   {
6644     int element = EL_CUSTOM_START + i;
6645
6646     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6647     {
6648       if (check < num_changed_custom_elements)
6649       {
6650         putFile16BitBE(file, element);
6651         putFile16BitBE(file, element_info[element].change->target_element);
6652       }
6653
6654       check++;
6655     }
6656   }
6657
6658   if (check != num_changed_custom_elements)     /* should not happen */
6659     Error(ERR_WARN, "inconsistent number of custom target elements");
6660 }
6661 #endif
6662
6663 #if ENABLE_HISTORIC_CHUNKS
6664 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6665                            int num_changed_custom_elements)
6666 {
6667   int i, j, x, y, check = 0;
6668
6669   putFile16BitBE(file, num_changed_custom_elements);
6670
6671   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6672   {
6673     int element = EL_CUSTOM_START + i;
6674     struct ElementInfo *ei = &element_info[element];
6675
6676     if (ei->modified_settings)
6677     {
6678       if (check < num_changed_custom_elements)
6679       {
6680         putFile16BitBE(file, element);
6681
6682         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6683           putFile8Bit(file, ei->description[j]);
6684
6685         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6686
6687         /* some free bytes for future properties and padding */
6688         WriteUnusedBytesToFile(file, 7);
6689
6690         putFile8Bit(file, ei->use_gfx_element);
6691         putFile16BitBE(file, ei->gfx_element_initial);
6692
6693         putFile8Bit(file, ei->collect_score_initial);
6694         putFile8Bit(file, ei->collect_count_initial);
6695
6696         putFile16BitBE(file, ei->push_delay_fixed);
6697         putFile16BitBE(file, ei->push_delay_random);
6698         putFile16BitBE(file, ei->move_delay_fixed);
6699         putFile16BitBE(file, ei->move_delay_random);
6700
6701         putFile16BitBE(file, ei->move_pattern);
6702         putFile8Bit(file, ei->move_direction_initial);
6703         putFile8Bit(file, ei->move_stepsize);
6704
6705         for (y = 0; y < 3; y++)
6706           for (x = 0; x < 3; x++)
6707             putFile16BitBE(file, ei->content.e[x][y]);
6708
6709         putFile32BitBE(file, ei->change->events);
6710
6711         putFile16BitBE(file, ei->change->target_element);
6712
6713         putFile16BitBE(file, ei->change->delay_fixed);
6714         putFile16BitBE(file, ei->change->delay_random);
6715         putFile16BitBE(file, ei->change->delay_frames);
6716
6717         putFile16BitBE(file, ei->change->initial_trigger_element);
6718
6719         putFile8Bit(file, ei->change->explode);
6720         putFile8Bit(file, ei->change->use_target_content);
6721         putFile8Bit(file, ei->change->only_if_complete);
6722         putFile8Bit(file, ei->change->use_random_replace);
6723
6724         putFile8Bit(file, ei->change->random_percentage);
6725         putFile8Bit(file, ei->change->replace_when);
6726
6727         for (y = 0; y < 3; y++)
6728           for (x = 0; x < 3; x++)
6729             putFile16BitBE(file, ei->change->content.e[x][y]);
6730
6731         putFile8Bit(file, ei->slippery_type);
6732
6733         /* some free bytes for future properties and padding */
6734         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6735       }
6736
6737       check++;
6738     }
6739   }
6740
6741   if (check != num_changed_custom_elements)     /* should not happen */
6742     Error(ERR_WARN, "inconsistent number of custom element properties");
6743 }
6744 #endif
6745
6746 #if ENABLE_HISTORIC_CHUNKS
6747 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6748 {
6749   struct ElementInfo *ei = &element_info[element];
6750   int i, j, x, y;
6751
6752   /* ---------- custom element base property values (96 bytes) ------------- */
6753
6754   putFile16BitBE(file, element);
6755
6756   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6757     putFile8Bit(file, ei->description[i]);
6758
6759   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6760
6761   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
6762
6763   putFile8Bit(file, ei->num_change_pages);
6764
6765   putFile16BitBE(file, ei->ce_value_fixed_initial);
6766   putFile16BitBE(file, ei->ce_value_random_initial);
6767   putFile8Bit(file, ei->use_last_ce_value);
6768
6769   putFile8Bit(file, ei->use_gfx_element);
6770   putFile16BitBE(file, ei->gfx_element_initial);
6771
6772   putFile8Bit(file, ei->collect_score_initial);
6773   putFile8Bit(file, ei->collect_count_initial);
6774
6775   putFile8Bit(file, ei->drop_delay_fixed);
6776   putFile8Bit(file, ei->push_delay_fixed);
6777   putFile8Bit(file, ei->drop_delay_random);
6778   putFile8Bit(file, ei->push_delay_random);
6779   putFile16BitBE(file, ei->move_delay_fixed);
6780   putFile16BitBE(file, ei->move_delay_random);
6781
6782   /* bits 0 - 15 of "move_pattern" ... */
6783   putFile16BitBE(file, ei->move_pattern & 0xffff);
6784   putFile8Bit(file, ei->move_direction_initial);
6785   putFile8Bit(file, ei->move_stepsize);
6786
6787   putFile8Bit(file, ei->slippery_type);
6788
6789   for (y = 0; y < 3; y++)
6790     for (x = 0; x < 3; x++)
6791       putFile16BitBE(file, ei->content.e[x][y]);
6792
6793   putFile16BitBE(file, ei->move_enter_element);
6794   putFile16BitBE(file, ei->move_leave_element);
6795   putFile8Bit(file, ei->move_leave_type);
6796
6797   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6798   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6799
6800   putFile8Bit(file, ei->access_direction);
6801
6802   putFile8Bit(file, ei->explosion_delay);
6803   putFile8Bit(file, ei->ignition_delay);
6804   putFile8Bit(file, ei->explosion_type);
6805
6806   /* some free bytes for future custom property values and padding */
6807   WriteUnusedBytesToFile(file, 1);
6808
6809   /* ---------- change page property values (48 bytes) --------------------- */
6810
6811   for (i = 0; i < ei->num_change_pages; i++)
6812   {
6813     struct ElementChangeInfo *change = &ei->change_page[i];
6814     unsigned int event_bits;
6815
6816     /* bits 0 - 31 of "has_event[]" ... */
6817     event_bits = 0;
6818     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6819       if (change->has_event[j])
6820         event_bits |= (1 << j);
6821     putFile32BitBE(file, event_bits);
6822
6823     putFile16BitBE(file, change->target_element);
6824
6825     putFile16BitBE(file, change->delay_fixed);
6826     putFile16BitBE(file, change->delay_random);
6827     putFile16BitBE(file, change->delay_frames);
6828
6829     putFile16BitBE(file, change->initial_trigger_element);
6830
6831     putFile8Bit(file, change->explode);
6832     putFile8Bit(file, change->use_target_content);
6833     putFile8Bit(file, change->only_if_complete);
6834     putFile8Bit(file, change->use_random_replace);
6835
6836     putFile8Bit(file, change->random_percentage);
6837     putFile8Bit(file, change->replace_when);
6838
6839     for (y = 0; y < 3; y++)
6840       for (x = 0; x < 3; x++)
6841         putFile16BitBE(file, change->target_content.e[x][y]);
6842
6843     putFile8Bit(file, change->can_change);
6844
6845     putFile8Bit(file, change->trigger_side);
6846
6847     putFile8Bit(file, change->trigger_player);
6848     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6849                        log_2(change->trigger_page)));
6850
6851     putFile8Bit(file, change->has_action);
6852     putFile8Bit(file, change->action_type);
6853     putFile8Bit(file, change->action_mode);
6854     putFile16BitBE(file, change->action_arg);
6855
6856     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6857     event_bits = 0;
6858     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6859       if (change->has_event[j])
6860         event_bits |= (1 << (j - 32));
6861     putFile8Bit(file, event_bits);
6862   }
6863 }
6864 #endif
6865
6866 #if ENABLE_HISTORIC_CHUNKS
6867 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6868 {
6869   struct ElementInfo *ei = &element_info[element];
6870   struct ElementGroupInfo *group = ei->group;
6871   int i;
6872
6873   putFile16BitBE(file, element);
6874
6875   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6876     putFile8Bit(file, ei->description[i]);
6877
6878   putFile8Bit(file, group->num_elements);
6879
6880   putFile8Bit(file, ei->use_gfx_element);
6881   putFile16BitBE(file, ei->gfx_element_initial);
6882
6883   putFile8Bit(file, group->choice_mode);
6884
6885   /* some free bytes for future values and padding */
6886   WriteUnusedBytesToFile(file, 3);
6887
6888   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6889     putFile16BitBE(file, group->element[i]);
6890 }
6891 #endif
6892
6893 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6894                                 boolean write_element)
6895 {
6896   int save_type = entry->save_type;
6897   int data_type = entry->data_type;
6898   int conf_type = entry->conf_type;
6899   int byte_mask = conf_type & CONF_MASK_BYTES;
6900   int element = entry->element;
6901   int default_value = entry->default_value;
6902   int num_bytes = 0;
6903   boolean modified = FALSE;
6904
6905   if (byte_mask != CONF_MASK_MULTI_BYTES)
6906   {
6907     void *value_ptr = entry->value;
6908     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6909                  *(int *)value_ptr);
6910
6911     /* check if any settings have been modified before saving them */
6912     if (value != default_value)
6913       modified = TRUE;
6914
6915     /* do not save if explicitly told or if unmodified default settings */
6916     if ((save_type == SAVE_CONF_NEVER) ||
6917         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6918       return 0;
6919
6920     if (write_element)
6921       num_bytes += putFile16BitBE(file, element);
6922
6923     num_bytes += putFile8Bit(file, conf_type);
6924     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
6925                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6926                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6927                   0);
6928   }
6929   else if (data_type == TYPE_STRING)
6930   {
6931     char *default_string = entry->default_string;
6932     char *string = (char *)(entry->value);
6933     int string_length = strlen(string);
6934     int i;
6935
6936     /* check if any settings have been modified before saving them */
6937     if (!strEqual(string, default_string))
6938       modified = TRUE;
6939
6940     /* do not save if explicitly told or if unmodified default settings */
6941     if ((save_type == SAVE_CONF_NEVER) ||
6942         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6943       return 0;
6944
6945     if (write_element)
6946       num_bytes += putFile16BitBE(file, element);
6947
6948     num_bytes += putFile8Bit(file, conf_type);
6949     num_bytes += putFile16BitBE(file, string_length);
6950
6951     for (i = 0; i < string_length; i++)
6952       num_bytes += putFile8Bit(file, string[i]);
6953   }
6954   else if (data_type == TYPE_ELEMENT_LIST)
6955   {
6956     int *element_array = (int *)(entry->value);
6957     int num_elements = *(int *)(entry->num_entities);
6958     int i;
6959
6960     /* check if any settings have been modified before saving them */
6961     for (i = 0; i < num_elements; i++)
6962       if (element_array[i] != default_value)
6963         modified = TRUE;
6964
6965     /* do not save if explicitly told or if unmodified default settings */
6966     if ((save_type == SAVE_CONF_NEVER) ||
6967         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6968       return 0;
6969
6970     if (write_element)
6971       num_bytes += putFile16BitBE(file, element);
6972
6973     num_bytes += putFile8Bit(file, conf_type);
6974     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6975
6976     for (i = 0; i < num_elements; i++)
6977       num_bytes += putFile16BitBE(file, element_array[i]);
6978   }
6979   else if (data_type == TYPE_CONTENT_LIST)
6980   {
6981     struct Content *content = (struct Content *)(entry->value);
6982     int num_contents = *(int *)(entry->num_entities);
6983     int i, x, y;
6984
6985     /* check if any settings have been modified before saving them */
6986     for (i = 0; i < num_contents; i++)
6987       for (y = 0; y < 3; y++)
6988         for (x = 0; x < 3; x++)
6989           if (content[i].e[x][y] != default_value)
6990             modified = TRUE;
6991
6992     /* do not save if explicitly told or if unmodified default settings */
6993     if ((save_type == SAVE_CONF_NEVER) ||
6994         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6995       return 0;
6996
6997     if (write_element)
6998       num_bytes += putFile16BitBE(file, element);
6999
7000     num_bytes += putFile8Bit(file, conf_type);
7001     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7002
7003     for (i = 0; i < num_contents; i++)
7004       for (y = 0; y < 3; y++)
7005         for (x = 0; x < 3; x++)
7006           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7007   }
7008
7009   return num_bytes;
7010 }
7011
7012 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7013 {
7014   int chunk_size = 0;
7015   int i;
7016
7017   li = *level;          /* copy level data into temporary buffer */
7018
7019   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7020     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7021
7022   return chunk_size;
7023 }
7024
7025 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7026 {
7027   int chunk_size = 0;
7028   int i;
7029
7030   li = *level;          /* copy level data into temporary buffer */
7031
7032   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7033     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7034
7035   return chunk_size;
7036 }
7037
7038 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7039 {
7040   int envelope_nr = element - EL_ENVELOPE_1;
7041   int chunk_size = 0;
7042   int i;
7043
7044   chunk_size += putFile16BitBE(file, element);
7045
7046   /* copy envelope data into temporary buffer */
7047   xx_envelope = level->envelope[envelope_nr];
7048
7049   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7050     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7051
7052   return chunk_size;
7053 }
7054
7055 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7056 {
7057   struct ElementInfo *ei = &element_info[element];
7058   int chunk_size = 0;
7059   int i, j;
7060
7061   chunk_size += putFile16BitBE(file, element);
7062
7063   xx_ei = *ei;          /* copy element data into temporary buffer */
7064
7065   /* set default description string for this specific element */
7066   strcpy(xx_default_description, getDefaultElementDescription(ei));
7067
7068   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7069     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7070
7071   for (i = 0; i < ei->num_change_pages; i++)
7072   {
7073     struct ElementChangeInfo *change = &ei->change_page[i];
7074
7075     xx_current_change_page = i;
7076
7077     xx_change = *change;        /* copy change data into temporary buffer */
7078
7079     resetEventBits();
7080     setEventBitsFromEventFlags(change);
7081
7082     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7083       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7084                                          FALSE);
7085   }
7086
7087   return chunk_size;
7088 }
7089
7090 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7091 {
7092   struct ElementInfo *ei = &element_info[element];
7093   struct ElementGroupInfo *group = ei->group;
7094   int chunk_size = 0;
7095   int i;
7096
7097   chunk_size += putFile16BitBE(file, element);
7098
7099   xx_ei = *ei;          /* copy element data into temporary buffer */
7100   xx_group = *group;    /* copy group data into temporary buffer */
7101
7102   /* set default description string for this specific element */
7103   strcpy(xx_default_description, getDefaultElementDescription(ei));
7104
7105   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7106     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7107
7108   return chunk_size;
7109 }
7110
7111 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7112                                   boolean save_as_template)
7113 {
7114   int chunk_size;
7115   int i;
7116   FILE *file;
7117
7118   if (!(file = fopen(filename, MODE_WRITE)))
7119   {
7120     Error(ERR_WARN, "cannot save level file '%s'", filename);
7121     return;
7122   }
7123
7124   level->file_version = FILE_VERSION_ACTUAL;
7125   level->game_version = GAME_VERSION_ACTUAL;
7126
7127   level->creation_date = getCurrentDate();
7128
7129   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7130   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7131
7132   chunk_size = SaveLevel_VERS(NULL, level);
7133   putFileChunkBE(file, "VERS", chunk_size);
7134   SaveLevel_VERS(file, level);
7135
7136   chunk_size = SaveLevel_DATE(NULL, level);
7137   putFileChunkBE(file, "DATE", chunk_size);
7138   SaveLevel_DATE(file, level);
7139
7140   chunk_size = SaveLevel_NAME(NULL, level);
7141   putFileChunkBE(file, "NAME", chunk_size);
7142   SaveLevel_NAME(file, level);
7143
7144   chunk_size = SaveLevel_AUTH(NULL, level);
7145   putFileChunkBE(file, "AUTH", chunk_size);
7146   SaveLevel_AUTH(file, level);
7147
7148   chunk_size = SaveLevel_INFO(NULL, level);
7149   putFileChunkBE(file, "INFO", chunk_size);
7150   SaveLevel_INFO(file, level);
7151
7152   chunk_size = SaveLevel_BODY(NULL, level);
7153   putFileChunkBE(file, "BODY", chunk_size);
7154   SaveLevel_BODY(file, level);
7155
7156   chunk_size = SaveLevel_ELEM(NULL, level);
7157   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7158   {
7159     putFileChunkBE(file, "ELEM", chunk_size);
7160     SaveLevel_ELEM(file, level);
7161   }
7162
7163   for (i = 0; i < NUM_ENVELOPES; i++)
7164   {
7165     int element = EL_ENVELOPE_1 + i;
7166
7167     chunk_size = SaveLevel_NOTE(NULL, level, element);
7168     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7169     {
7170       putFileChunkBE(file, "NOTE", chunk_size);
7171       SaveLevel_NOTE(file, level, element);
7172     }
7173   }
7174
7175   /* if not using template level, check for non-default custom/group elements */
7176   if (!level->use_custom_template || save_as_template)
7177   {
7178     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7179     {
7180       int element = EL_CUSTOM_START + i;
7181
7182       chunk_size = SaveLevel_CUSX(NULL, level, element);
7183       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7184       {
7185         putFileChunkBE(file, "CUSX", chunk_size);
7186         SaveLevel_CUSX(file, level, element);
7187       }
7188     }
7189
7190     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7191     {
7192       int element = EL_GROUP_START + i;
7193
7194       chunk_size = SaveLevel_GRPX(NULL, level, element);
7195       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7196       {
7197         putFileChunkBE(file, "GRPX", chunk_size);
7198         SaveLevel_GRPX(file, level, element);
7199       }
7200     }
7201   }
7202
7203   fclose(file);
7204
7205   SetFilePermissions(filename, PERMS_PRIVATE);
7206 }
7207
7208 void SaveLevel(int nr)
7209 {
7210   char *filename = getDefaultLevelFilename(nr);
7211
7212   SaveLevelFromFilename(&level, filename, FALSE);
7213 }
7214
7215 void SaveLevelTemplate()
7216 {
7217   char *filename = getLocalLevelTemplateFilename();
7218
7219   SaveLevelFromFilename(&level, filename, TRUE);
7220 }
7221
7222 boolean SaveLevelChecked(int nr)
7223 {
7224   char *filename = getDefaultLevelFilename(nr);
7225   boolean new_level = !fileExists(filename);
7226   boolean level_saved = FALSE;
7227
7228   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7229   {
7230     SaveLevel(nr);
7231
7232     if (new_level)
7233       Request("Level saved!", REQ_CONFIRM);
7234
7235     level_saved = TRUE;
7236   }
7237
7238   return level_saved;
7239 }
7240
7241 void DumpLevel(struct LevelInfo *level)
7242 {
7243   if (level->no_level_file || level->no_valid_file)
7244   {
7245     Error(ERR_WARN, "cannot dump -- no valid level file found");
7246
7247     return;
7248   }
7249
7250   PrintLine("-", 79);
7251   Print("Level xxx (file version %08d, game version %08d)\n",
7252         level->file_version, level->game_version);
7253   PrintLine("-", 79);
7254
7255   Print("Level author: '%s'\n", level->author);
7256   Print("Level title:  '%s'\n", level->name);
7257   Print("\n");
7258   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7259   Print("\n");
7260   Print("Level time:  %d seconds\n", level->time);
7261   Print("Gems needed: %d\n", level->gems_needed);
7262   Print("\n");
7263   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7264   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7265   Print("Time for light:      %d seconds\n", level->time_light);
7266   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7267   Print("\n");
7268   Print("Amoeba speed: %d\n", level->amoeba_speed);
7269   Print("\n");
7270
7271   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7272   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7273   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7274   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7275   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7276
7277   PrintLine("-", 79);
7278 }
7279
7280
7281 /* ========================================================================= */
7282 /* tape file functions                                                       */
7283 /* ========================================================================= */
7284
7285 static void setTapeInfoToDefaults()
7286 {
7287   int i;
7288
7289   /* always start with reliable default values (empty tape) */
7290   TapeErase();
7291
7292   /* default values (also for pre-1.2 tapes) with only the first player */
7293   tape.player_participates[0] = TRUE;
7294   for (i = 1; i < MAX_PLAYERS; i++)
7295     tape.player_participates[i] = FALSE;
7296
7297   /* at least one (default: the first) player participates in every tape */
7298   tape.num_participating_players = 1;
7299
7300   tape.level_nr = level_nr;
7301   tape.counter = 0;
7302   tape.changed = FALSE;
7303
7304   tape.recording = FALSE;
7305   tape.playing = FALSE;
7306   tape.pausing = FALSE;
7307
7308   tape.no_valid_file = FALSE;
7309 }
7310
7311 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7312 {
7313   tape->file_version = getFileVersion(file);
7314   tape->game_version = getFileVersion(file);
7315
7316   return chunk_size;
7317 }
7318
7319 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7320 {
7321   int i;
7322
7323   tape->random_seed = getFile32BitBE(file);
7324   tape->date        = getFile32BitBE(file);
7325   tape->length      = getFile32BitBE(file);
7326
7327   /* read header fields that are new since version 1.2 */
7328   if (tape->file_version >= FILE_VERSION_1_2)
7329   {
7330     byte store_participating_players = getFile8Bit(file);
7331     int engine_version;
7332
7333     /* since version 1.2, tapes store which players participate in the tape */
7334     tape->num_participating_players = 0;
7335     for (i = 0; i < MAX_PLAYERS; i++)
7336     {
7337       tape->player_participates[i] = FALSE;
7338
7339       if (store_participating_players & (1 << i))
7340       {
7341         tape->player_participates[i] = TRUE;
7342         tape->num_participating_players++;
7343       }
7344     }
7345
7346     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7347
7348     engine_version = getFileVersion(file);
7349     if (engine_version > 0)
7350       tape->engine_version = engine_version;
7351     else
7352       tape->engine_version = tape->game_version;
7353   }
7354
7355   return chunk_size;
7356 }
7357
7358 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7359 {
7360   int level_identifier_size;
7361   int i;
7362
7363   level_identifier_size = getFile16BitBE(file);
7364
7365   tape->level_identifier =
7366     checked_realloc(tape->level_identifier, level_identifier_size);
7367
7368   for (i = 0; i < level_identifier_size; i++)
7369     tape->level_identifier[i] = getFile8Bit(file);
7370
7371   tape->level_nr = getFile16BitBE(file);
7372
7373   chunk_size = 2 + level_identifier_size + 2;
7374
7375   return chunk_size;
7376 }
7377
7378 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7379 {
7380   int i, j;
7381   int chunk_size_expected =
7382     (tape->num_participating_players + 1) * tape->length;
7383
7384   if (chunk_size_expected != chunk_size)
7385   {
7386     ReadUnusedBytesFromFile(file, chunk_size);
7387     return chunk_size_expected;
7388   }
7389
7390   for (i = 0; i < tape->length; i++)
7391   {
7392     if (i >= MAX_TAPE_LEN)
7393     {
7394       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7395             MAX_TAPE_LEN);
7396
7397       // tape too large; read and ignore remaining tape data from this chunk
7398       for (;i < tape->length; i++)
7399         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7400
7401       break;
7402     }
7403
7404     for (j = 0; j < MAX_PLAYERS; j++)
7405     {
7406       tape->pos[i].action[j] = MV_NONE;
7407
7408       if (tape->player_participates[j])
7409         tape->pos[i].action[j] = getFile8Bit(file);
7410     }
7411
7412     tape->pos[i].delay = getFile8Bit(file);
7413
7414     if (tape->file_version == FILE_VERSION_1_0)
7415     {
7416       /* eliminate possible diagonal moves in old tapes */
7417       /* this is only for backward compatibility */
7418
7419       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7420       byte action = tape->pos[i].action[0];
7421       int k, num_moves = 0;
7422
7423       for (k = 0; k<4; k++)
7424       {
7425         if (action & joy_dir[k])
7426         {
7427           tape->pos[i + num_moves].action[0] = joy_dir[k];
7428           if (num_moves > 0)
7429             tape->pos[i + num_moves].delay = 0;
7430           num_moves++;
7431         }
7432       }
7433
7434       if (num_moves > 1)
7435       {
7436         num_moves--;
7437         i += num_moves;
7438         tape->length += num_moves;
7439       }
7440     }
7441     else if (tape->file_version < FILE_VERSION_2_0)
7442     {
7443       /* convert pre-2.0 tapes to new tape format */
7444
7445       if (tape->pos[i].delay > 1)
7446       {
7447         /* action part */
7448         tape->pos[i + 1] = tape->pos[i];
7449         tape->pos[i + 1].delay = 1;
7450
7451         /* delay part */
7452         for (j = 0; j < MAX_PLAYERS; j++)
7453           tape->pos[i].action[j] = MV_NONE;
7454         tape->pos[i].delay--;
7455
7456         i++;
7457         tape->length++;
7458       }
7459     }
7460
7461     if (checkEndOfFile(file))
7462       break;
7463   }
7464
7465   if (i != tape->length)
7466     chunk_size = (tape->num_participating_players + 1) * i;
7467
7468   return chunk_size;
7469 }
7470
7471 void LoadTape_SokobanSolution(char *filename)
7472 {
7473   File *file;
7474   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7475
7476   if (!(file = openFile(filename, MODE_READ)))
7477   {
7478     tape.no_valid_file = TRUE;
7479
7480     return;
7481   }
7482
7483   while (!checkEndOfFile(file))
7484   {
7485     unsigned char c = getByteFromFile(file);
7486
7487     if (checkEndOfFile(file))
7488       break;
7489
7490     switch (c)
7491     {
7492       case 'u':
7493       case 'U':
7494         tape.pos[tape.length].action[0] = MV_UP;
7495         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7496         tape.length++;
7497         break;
7498
7499       case 'd':
7500       case 'D':
7501         tape.pos[tape.length].action[0] = MV_DOWN;
7502         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7503         tape.length++;
7504         break;
7505
7506       case 'l':
7507       case 'L':
7508         tape.pos[tape.length].action[0] = MV_LEFT;
7509         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7510         tape.length++;
7511         break;
7512
7513       case 'r':
7514       case 'R':
7515         tape.pos[tape.length].action[0] = MV_RIGHT;
7516         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7517         tape.length++;
7518         break;
7519
7520       case '\n':
7521       case '\r':
7522       case '\t':
7523       case ' ':
7524         /* ignore white-space characters */
7525         break;
7526
7527       default:
7528         tape.no_valid_file = TRUE;
7529
7530         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7531
7532         break;
7533     }
7534   }
7535
7536   closeFile(file);
7537
7538   if (tape.no_valid_file)
7539     return;
7540
7541   tape.length_frames  = GetTapeLengthFrames();
7542   tape.length_seconds = GetTapeLengthSeconds();
7543 }
7544
7545 void LoadTapeFromFilename(char *filename)
7546 {
7547   char cookie[MAX_LINE_LEN];
7548   char chunk_name[CHUNK_ID_LEN + 1];
7549   File *file;
7550   int chunk_size;
7551
7552   /* always start with reliable default values */
7553   setTapeInfoToDefaults();
7554
7555   if (strSuffix(filename, ".sln"))
7556   {
7557     LoadTape_SokobanSolution(filename);
7558
7559     return;
7560   }
7561
7562   if (!(file = openFile(filename, MODE_READ)))
7563   {
7564     tape.no_valid_file = TRUE;
7565
7566     return;
7567   }
7568
7569   getFileChunkBE(file, chunk_name, NULL);
7570   if (strEqual(chunk_name, "RND1"))
7571   {
7572     getFile32BitBE(file);               /* not used */
7573
7574     getFileChunkBE(file, chunk_name, NULL);
7575     if (!strEqual(chunk_name, "TAPE"))
7576     {
7577       tape.no_valid_file = TRUE;
7578
7579       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7580
7581       closeFile(file);
7582
7583       return;
7584     }
7585   }
7586   else  /* check for pre-2.0 file format with cookie string */
7587   {
7588     strcpy(cookie, chunk_name);
7589     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7590       cookie[4] = '\0';
7591     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7592       cookie[strlen(cookie) - 1] = '\0';
7593
7594     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7595     {
7596       tape.no_valid_file = TRUE;
7597
7598       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7599
7600       closeFile(file);
7601
7602       return;
7603     }
7604
7605     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7606     {
7607       tape.no_valid_file = TRUE;
7608
7609       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7610
7611       closeFile(file);
7612
7613       return;
7614     }
7615
7616     /* pre-2.0 tape files have no game version, so use file version here */
7617     tape.game_version = tape.file_version;
7618   }
7619
7620   if (tape.file_version < FILE_VERSION_1_2)
7621   {
7622     /* tape files from versions before 1.2.0 without chunk structure */
7623     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7624     LoadTape_BODY(file, 2 * tape.length,      &tape);
7625   }
7626   else
7627   {
7628     static struct
7629     {
7630       char *name;
7631       int size;
7632       int (*loader)(File *, int, struct TapeInfo *);
7633     }
7634     chunk_info[] =
7635     {
7636       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7637       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7638       { "INFO", -1,                     LoadTape_INFO },
7639       { "BODY", -1,                     LoadTape_BODY },
7640       {  NULL,  0,                      NULL }
7641     };
7642
7643     while (getFileChunkBE(file, chunk_name, &chunk_size))
7644     {
7645       int i = 0;
7646
7647       while (chunk_info[i].name != NULL &&
7648              !strEqual(chunk_name, chunk_info[i].name))
7649         i++;
7650
7651       if (chunk_info[i].name == NULL)
7652       {
7653         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7654               chunk_name, filename);
7655         ReadUnusedBytesFromFile(file, chunk_size);
7656       }
7657       else if (chunk_info[i].size != -1 &&
7658                chunk_info[i].size != chunk_size)
7659       {
7660         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7661               chunk_size, chunk_name, filename);
7662         ReadUnusedBytesFromFile(file, chunk_size);
7663       }
7664       else
7665       {
7666         /* call function to load this tape chunk */
7667         int chunk_size_expected =
7668           (chunk_info[i].loader)(file, chunk_size, &tape);
7669
7670         /* the size of some chunks cannot be checked before reading other
7671            chunks first (like "HEAD" and "BODY") that contain some header
7672            information, so check them here */
7673         if (chunk_size_expected != chunk_size)
7674         {
7675           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7676                 chunk_size, chunk_name, filename);
7677         }
7678       }
7679     }
7680   }
7681
7682   closeFile(file);
7683
7684   tape.length_frames  = GetTapeLengthFrames();
7685   tape.length_seconds = GetTapeLengthSeconds();
7686
7687 #if 0
7688   printf("::: tape file version: %d\n",   tape.file_version);
7689   printf("::: tape game version: %d\n",   tape.game_version);
7690   printf("::: tape engine version: %d\n", tape.engine_version);
7691 #endif
7692 }
7693
7694 void LoadTape(int nr)
7695 {
7696   char *filename = getTapeFilename(nr);
7697
7698   LoadTapeFromFilename(filename);
7699 }
7700
7701 void LoadSolutionTape(int nr)
7702 {
7703   char *filename = getSolutionTapeFilename(nr);
7704
7705   LoadTapeFromFilename(filename);
7706
7707   if (TAPE_IS_EMPTY(tape) &&
7708       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7709       level.native_sp_level->demo.is_available)
7710     CopyNativeTape_SP_to_RND(&level);
7711 }
7712
7713 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7714 {
7715   putFileVersion(file, tape->file_version);
7716   putFileVersion(file, tape->game_version);
7717 }
7718
7719 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7720 {
7721   int i;
7722   byte store_participating_players = 0;
7723
7724   /* set bits for participating players for compact storage */
7725   for (i = 0; i < MAX_PLAYERS; i++)
7726     if (tape->player_participates[i])
7727       store_participating_players |= (1 << i);
7728
7729   putFile32BitBE(file, tape->random_seed);
7730   putFile32BitBE(file, tape->date);
7731   putFile32BitBE(file, tape->length);
7732
7733   putFile8Bit(file, store_participating_players);
7734
7735   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7736   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7737
7738   putFileVersion(file, tape->engine_version);
7739 }
7740
7741 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7742 {
7743   int level_identifier_size = strlen(tape->level_identifier) + 1;
7744   int i;
7745
7746   putFile16BitBE(file, level_identifier_size);
7747
7748   for (i = 0; i < level_identifier_size; i++)
7749     putFile8Bit(file, tape->level_identifier[i]);
7750
7751   putFile16BitBE(file, tape->level_nr);
7752 }
7753
7754 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7755 {
7756   int i, j;
7757
7758   for (i = 0; i < tape->length; i++)
7759   {
7760     for (j = 0; j < MAX_PLAYERS; j++)
7761       if (tape->player_participates[j])
7762         putFile8Bit(file, tape->pos[i].action[j]);
7763
7764     putFile8Bit(file, tape->pos[i].delay);
7765   }
7766 }
7767
7768 void SaveTape(int nr)
7769 {
7770   char *filename = getTapeFilename(nr);
7771   FILE *file;
7772   int num_participating_players = 0;
7773   int info_chunk_size;
7774   int body_chunk_size;
7775   int i;
7776
7777   InitTapeDirectory(leveldir_current->subdir);
7778
7779   if (!(file = fopen(filename, MODE_WRITE)))
7780   {
7781     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7782     return;
7783   }
7784
7785   tape.file_version = FILE_VERSION_ACTUAL;
7786   tape.game_version = GAME_VERSION_ACTUAL;
7787
7788   /* count number of participating players  */
7789   for (i = 0; i < MAX_PLAYERS; i++)
7790     if (tape.player_participates[i])
7791       num_participating_players++;
7792
7793   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7794   body_chunk_size = (num_participating_players + 1) * tape.length;
7795
7796   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7797   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7798
7799   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7800   SaveTape_VERS(file, &tape);
7801
7802   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7803   SaveTape_HEAD(file, &tape);
7804
7805   putFileChunkBE(file, "INFO", info_chunk_size);
7806   SaveTape_INFO(file, &tape);
7807
7808   putFileChunkBE(file, "BODY", body_chunk_size);
7809   SaveTape_BODY(file, &tape);
7810
7811   fclose(file);
7812
7813   SetFilePermissions(filename, PERMS_PRIVATE);
7814
7815   tape.changed = FALSE;
7816 }
7817
7818 boolean SaveTapeChecked(int nr)
7819 {
7820   char *filename = getTapeFilename(nr);
7821   boolean new_tape = !fileExists(filename);
7822   boolean tape_saved = FALSE;
7823
7824   if (new_tape || Request("Replace old tape?", REQ_ASK))
7825   {
7826     SaveTape(nr);
7827
7828     if (new_tape)
7829       Request("Tape saved!", REQ_CONFIRM);
7830
7831     tape_saved = TRUE;
7832   }
7833
7834   return tape_saved;
7835 }
7836
7837 void DumpTape(struct TapeInfo *tape)
7838 {
7839   int tape_frame_counter;
7840   int i, j;
7841
7842   if (tape->no_valid_file)
7843   {
7844     Error(ERR_WARN, "cannot dump -- no valid tape file found");
7845
7846     return;
7847   }
7848
7849   PrintLine("-", 79);
7850   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7851         tape->level_nr, tape->file_version, tape->game_version);
7852   Print("                  (effective engine version %08d)\n",
7853         tape->engine_version);
7854   Print("Level series identifier: '%s'\n", tape->level_identifier);
7855   PrintLine("-", 79);
7856
7857   tape_frame_counter = 0;
7858
7859   for (i = 0; i < tape->length; i++)
7860   {
7861     if (i >= MAX_TAPE_LEN)
7862       break;
7863
7864     Print("%04d: ", i);
7865
7866     for (j = 0; j < MAX_PLAYERS; j++)
7867     {
7868       if (tape->player_participates[j])
7869       {
7870         int action = tape->pos[i].action[j];
7871
7872         Print("%d:%02x ", j, action);
7873         Print("[%c%c%c%c|%c%c] - ",
7874               (action & JOY_LEFT ? '<' : ' '),
7875               (action & JOY_RIGHT ? '>' : ' '),
7876               (action & JOY_UP ? '^' : ' '),
7877               (action & JOY_DOWN ? 'v' : ' '),
7878               (action & JOY_BUTTON_1 ? '1' : ' '),
7879               (action & JOY_BUTTON_2 ? '2' : ' '));
7880       }
7881     }
7882
7883     Print("(%03d) ", tape->pos[i].delay);
7884     Print("[%05d]\n", tape_frame_counter);
7885
7886     tape_frame_counter += tape->pos[i].delay;
7887   }
7888
7889   PrintLine("-", 79);
7890 }
7891
7892
7893 /* ========================================================================= */
7894 /* score file functions                                                      */
7895 /* ========================================================================= */
7896
7897 void LoadScore(int nr)
7898 {
7899   int i;
7900   char *filename = getScoreFilename(nr);
7901   char cookie[MAX_LINE_LEN];
7902   char line[MAX_LINE_LEN];
7903   char *line_ptr;
7904   FILE *file;
7905
7906   /* always start with reliable default values */
7907   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7908   {
7909     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7910     highscore[i].Score = 0;
7911   }
7912
7913   if (!(file = fopen(filename, MODE_READ)))
7914     return;
7915
7916   /* check file identifier */
7917   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7918     cookie[0] = '\0';
7919   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7920     cookie[strlen(cookie) - 1] = '\0';
7921
7922   if (!checkCookieString(cookie, SCORE_COOKIE))
7923   {
7924     Error(ERR_WARN, "unknown format of score file '%s'", filename);
7925     fclose(file);
7926     return;
7927   }
7928
7929   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7930   {
7931     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7932       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7933     if (fgets(line, MAX_LINE_LEN, file) == NULL)
7934       line[0] = '\0';
7935
7936     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7937       line[strlen(line) - 1] = '\0';
7938
7939     for (line_ptr = line; *line_ptr; line_ptr++)
7940     {
7941       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7942       {
7943         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7944         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7945         break;
7946       }
7947     }
7948   }
7949
7950   fclose(file);
7951 }
7952
7953 void SaveScore(int nr)
7954 {
7955   int i;
7956   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7957   char *filename = getScoreFilename(nr);
7958   FILE *file;
7959
7960   InitScoreDirectory(leveldir_current->subdir);
7961
7962   if (!(file = fopen(filename, MODE_WRITE)))
7963   {
7964     Error(ERR_WARN, "cannot save score for level %d", nr);
7965     return;
7966   }
7967
7968   fprintf(file, "%s\n\n", SCORE_COOKIE);
7969
7970   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7971     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7972
7973   fclose(file);
7974
7975   SetFilePermissions(filename, permissions);
7976 }
7977
7978
7979 /* ========================================================================= */
7980 /* setup file functions                                                      */
7981 /* ========================================================================= */
7982
7983 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
7984
7985 /* global setup */
7986 #define SETUP_TOKEN_PLAYER_NAME                 0
7987 #define SETUP_TOKEN_SOUND                       1
7988 #define SETUP_TOKEN_SOUND_LOOPS                 2
7989 #define SETUP_TOKEN_SOUND_MUSIC                 3
7990 #define SETUP_TOKEN_SOUND_SIMPLE                4
7991 #define SETUP_TOKEN_TOONS                       5
7992 #define SETUP_TOKEN_SCROLL_DELAY                6
7993 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
7994 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
7995 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
7996 #define SETUP_TOKEN_FADE_SCREENS                10
7997 #define SETUP_TOKEN_AUTORECORD                  11
7998 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
7999 #define SETUP_TOKEN_QUICK_DOORS                 13
8000 #define SETUP_TOKEN_TEAM_MODE                   14
8001 #define SETUP_TOKEN_HANDICAP                    15
8002 #define SETUP_TOKEN_SKIP_LEVELS                 16
8003 #define SETUP_TOKEN_INCREMENT_LEVELS            17
8004 #define SETUP_TOKEN_TIME_LIMIT                  18
8005 #define SETUP_TOKEN_FULLSCREEN                  19
8006 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
8007 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
8008 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8009 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8010 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8011 #define SETUP_TOKEN_QUICK_SWITCH                25
8012 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8013 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8014 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8015 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8016 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8017 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8018 #define SETUP_TOKEN_GRAPHICS_SET                32
8019 #define SETUP_TOKEN_SOUNDS_SET                  33
8020 #define SETUP_TOKEN_MUSIC_SET                   34
8021 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8022 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8023 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8024 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8025 #define SETUP_TOKEN_VOLUME_LOOPS                39
8026 #define SETUP_TOKEN_VOLUME_MUSIC                40
8027 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8028 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8029 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8030
8031 #define NUM_GLOBAL_SETUP_TOKENS                 44
8032
8033 /* editor setup */
8034 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8035 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8036 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8037 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8038 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8039 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8040
8041 #define NUM_EDITOR_SETUP_TOKENS                 6
8042
8043 /* editor cascade setup */
8044 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8045 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8046 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8047 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8048 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8049 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8050 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8051 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8052 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8053 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8054 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8055 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8056 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8057 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8058 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8059
8060 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8061
8062 /* shortcut setup */
8063 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8064 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8065 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8066 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8067 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8068 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8069 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8070 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8071 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8072 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8073 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8074 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8075 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8076 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8077 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8078 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8079 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8080 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8081 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8082 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8083 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8084
8085 #define NUM_SHORTCUT_SETUP_TOKENS               21
8086
8087 /* player setup */
8088 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8089 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8090 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8091 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8092 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8093 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8094 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8095 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8096 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8097 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8098 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8099 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8100 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8101 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8102 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8103 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8104
8105 #define NUM_PLAYER_SETUP_TOKENS                 16
8106
8107 /* system setup */
8108 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8109 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8110 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8111
8112 #define NUM_SYSTEM_SETUP_TOKENS                 3
8113
8114 /* internal setup */
8115 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8116 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8117 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8118 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8119 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8120 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8121 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8122 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8123 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8124 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8125 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8126 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8127 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8128 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8129 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8130 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8131 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8132 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8133 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8134
8135 #define NUM_INTERNAL_SETUP_TOKENS               19
8136
8137 /* debug setup */
8138 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8139 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8140 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8141 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8142 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8143 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8144 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8145 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8146 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8147 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8148 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8149 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8150 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8151 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8152 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8153 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8154 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8155 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8156 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8157 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8158 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8159 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8160 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8161
8162 #define NUM_DEBUG_SETUP_TOKENS                  23
8163
8164 /* options setup */
8165 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8166
8167 #define NUM_OPTIONS_SETUP_TOKENS                1
8168
8169
8170 static struct SetupInfo si;
8171 static struct SetupEditorInfo sei;
8172 static struct SetupEditorCascadeInfo seci;
8173 static struct SetupShortcutInfo ssi;
8174 static struct SetupInputInfo sii;
8175 static struct SetupSystemInfo syi;
8176 static struct SetupInternalInfo sxi;
8177 static struct SetupDebugInfo sdi;
8178 static struct OptionInfo soi;
8179
8180 static struct TokenInfo global_setup_tokens[] =
8181 {
8182   { TYPE_STRING, &si.player_name,             "player_name"             },
8183   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8184   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8185   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8186   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8187   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8188   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8189   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8190   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8191   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8192   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8193   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8194   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8195   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8196   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8197   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8198   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8199   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8200   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8201   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8202   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8203   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8204   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8205   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8206   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8207   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8208   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8209   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8210   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8211   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8212   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8213   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8214   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8215   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8216   { TYPE_STRING, &si.music_set,               "music_set"               },
8217   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8218   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8219   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8220   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8221   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8222   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8223   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8224   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8225   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8226 };
8227
8228 static struct TokenInfo editor_setup_tokens[] =
8229 {
8230   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8231   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8232   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8233   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8234   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8235   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8236 };
8237
8238 static struct TokenInfo editor_cascade_setup_tokens[] =
8239 {
8240   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8241   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8242   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8243   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8244   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8245   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8246   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8247   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8248   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8249   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8250   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8251   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8252   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8253   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8254   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8255 };
8256
8257 static struct TokenInfo shortcut_setup_tokens[] =
8258 {
8259   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8260   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8261   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8262   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8263   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8264   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8265   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8266   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8267   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8268   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8269   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8270   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8271   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8272   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8273   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8274   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8275   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8276   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8277   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8278   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8279   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8280 };
8281
8282 static struct TokenInfo player_setup_tokens[] =
8283 {
8284   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8285   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8286   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8287   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8288   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8289   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8290   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8291   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8292   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8293   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8294   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8295   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8296   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8297   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8298   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8299   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8300 };
8301
8302 static struct TokenInfo system_setup_tokens[] =
8303 {
8304   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8305   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8306   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8307 };
8308
8309 static struct TokenInfo internal_setup_tokens[] =
8310 {
8311   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8312   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8313   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8314   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8315   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8316   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8317   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8318   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8319   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8320   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8321   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8322   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8323   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8324   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8325   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8326   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8327   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8328   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8329   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8330 };
8331
8332 static struct TokenInfo debug_setup_tokens[] =
8333 {
8334   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8335   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8336   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8337   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8338   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8339   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8340   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8341   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8342   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8343   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8344   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8345   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8346   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8347   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8348   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8349   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8350   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8351   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8352   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8353   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8354   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8355   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8356   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8357 };
8358
8359 static struct TokenInfo options_setup_tokens[] =
8360 {
8361   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8362 };
8363
8364 static char *get_corrected_login_name(char *login_name)
8365 {
8366   /* needed because player name must be a fixed length string */
8367   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8368
8369   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8370   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8371
8372   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8373     if (strchr(login_name_new, ' '))
8374       *strchr(login_name_new, ' ') = '\0';
8375
8376   return login_name_new;
8377 }
8378
8379 static void setSetupInfoToDefaults(struct SetupInfo *si)
8380 {
8381   int i;
8382
8383   si->player_name = get_corrected_login_name(getLoginName());
8384
8385   si->sound = TRUE;
8386   si->sound_loops = TRUE;
8387   si->sound_music = TRUE;
8388   si->sound_simple = TRUE;
8389   si->toons = TRUE;
8390   si->scroll_delay = TRUE;
8391   si->scroll_delay_value = STD_SCROLL_DELAY;
8392   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8393   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8394   si->fade_screens = TRUE;
8395   si->autorecord = TRUE;
8396   si->show_titlescreen = TRUE;
8397   si->quick_doors = FALSE;
8398   si->team_mode = FALSE;
8399   si->handicap = TRUE;
8400   si->skip_levels = TRUE;
8401   si->increment_levels = TRUE;
8402   si->time_limit = TRUE;
8403   si->fullscreen = FALSE;
8404   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8405   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8406   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8407   si->ask_on_escape = TRUE;
8408   si->ask_on_escape_editor = TRUE;
8409   si->quick_switch = FALSE;
8410   si->input_on_focus = FALSE;
8411   si->prefer_aga_graphics = TRUE;
8412   si->game_frame_delay = GAME_FRAME_DELAY;
8413   si->sp_show_border_elements = FALSE;
8414   si->small_game_graphics = FALSE;
8415   si->show_snapshot_buttons = FALSE;
8416
8417   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8418   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8419   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8420
8421   si->override_level_graphics = FALSE;
8422   si->override_level_sounds = FALSE;
8423   si->override_level_music = FALSE;
8424
8425   si->volume_simple = 100;              /* percent */
8426   si->volume_loops = 100;               /* percent */
8427   si->volume_music = 100;               /* percent */
8428
8429   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8430   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8431   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8432
8433   si->editor.el_boulderdash             = TRUE;
8434   si->editor.el_emerald_mine            = TRUE;
8435   si->editor.el_emerald_mine_club       = TRUE;
8436   si->editor.el_more                    = TRUE;
8437   si->editor.el_sokoban                 = TRUE;
8438   si->editor.el_supaplex                = TRUE;
8439   si->editor.el_diamond_caves           = TRUE;
8440   si->editor.el_dx_boulderdash          = TRUE;
8441   si->editor.el_chars                   = TRUE;
8442   si->editor.el_steel_chars             = TRUE;
8443
8444   si->editor.el_classic                 = TRUE;
8445   si->editor.el_custom                  = TRUE;
8446
8447   si->editor.el_user_defined            = FALSE;
8448   si->editor.el_dynamic                 = TRUE;
8449
8450   si->editor.el_headlines               = TRUE;
8451
8452   si->editor.show_element_token         = FALSE;
8453
8454   si->editor.use_template_for_new_levels = TRUE;
8455
8456   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8457   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8458   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8459
8460   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8461   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8462   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8463   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8464   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8465
8466   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8467   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8468   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8469   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8470   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8471   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8472
8473   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8474   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8475   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8476
8477   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8478   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8479   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8480   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8481
8482   for (i = 0; i < MAX_PLAYERS; i++)
8483   {
8484     si->input[i].use_joystick = FALSE;
8485     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8486     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8487     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8488     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8489     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8490     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8491     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8492     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8493     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8494     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8495     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8496     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8497     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8498     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8499     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8500   }
8501
8502   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8503   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8504   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8505
8506   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8507   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8508   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8509   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8510   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8511   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8512   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8513
8514   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8515
8516   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8517   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8518   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8519
8520   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8521   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8522   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8523
8524   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8525   si->internal.choose_from_top_leveldir = FALSE;
8526   si->internal.show_scaling_in_title = TRUE;
8527
8528   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8529   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8530
8531   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8532   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8533   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8534   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8535   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8536   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8537   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8538   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8539   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8540   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8541
8542   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8543   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8544   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8545   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8546   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8547   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8548   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8549   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8550   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8551   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8552
8553   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8554   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8555
8556   si->debug.show_frames_per_second = FALSE;
8557
8558   si->options.verbose = FALSE;
8559
8560 #if defined(PLATFORM_ANDROID)
8561   si->fullscreen = TRUE;
8562 #endif
8563 }
8564
8565 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8566 {
8567   si->editor_cascade.el_bd              = TRUE;
8568   si->editor_cascade.el_em              = TRUE;
8569   si->editor_cascade.el_emc             = TRUE;
8570   si->editor_cascade.el_rnd             = TRUE;
8571   si->editor_cascade.el_sb              = TRUE;
8572   si->editor_cascade.el_sp              = TRUE;
8573   si->editor_cascade.el_dc              = TRUE;
8574   si->editor_cascade.el_dx              = TRUE;
8575
8576   si->editor_cascade.el_chars           = FALSE;
8577   si->editor_cascade.el_steel_chars     = FALSE;
8578   si->editor_cascade.el_ce              = FALSE;
8579   si->editor_cascade.el_ge              = FALSE;
8580   si->editor_cascade.el_ref             = FALSE;
8581   si->editor_cascade.el_user            = FALSE;
8582   si->editor_cascade.el_dynamic         = FALSE;
8583 }
8584
8585 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8586
8587 static char *getHideSetupToken(void *setup_value)
8588 {
8589   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8590
8591   if (setup_value != NULL)
8592     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8593
8594   return hide_setup_token;
8595 }
8596
8597 static void setHideSetupEntry(void *setup_value_raw)
8598 {
8599   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8600   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8601
8602   char *hide_setup_token = getHideSetupToken(setup_value);
8603
8604   if (setup_value != NULL)
8605     setHashEntry(hide_setup_hash, hide_setup_token, "");
8606 }
8607
8608 boolean hideSetupEntry(void *setup_value)
8609 {
8610   char *hide_setup_token = getHideSetupToken(setup_value);
8611
8612   return (setup_value != NULL &&
8613           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8614 }
8615
8616 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8617                                       struct TokenInfo *token_info,
8618                                       int token_nr, char *token_text)
8619 {
8620   char *token_hide_text = getStringCat2(token_text, ".hide");
8621   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8622
8623   /* set the value of this setup option in the setup option structure */
8624   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8625
8626   /* check if this setup option should be hidden in the setup menu */
8627   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8628     setHideSetupEntry(token_info[token_nr].value);
8629 }
8630
8631 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8632                                       struct TokenInfo *token_info,
8633                                       int token_nr)
8634 {
8635   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8636                             token_info[token_nr].text);
8637 }
8638
8639 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8640 {
8641   int i, pnr;
8642
8643   if (!setup_file_hash)
8644     return;
8645
8646   if (hide_setup_hash == NULL)
8647     hide_setup_hash = newSetupFileHash();
8648
8649   /* global setup */
8650   si = setup;
8651   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8652     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8653   setup = si;
8654
8655   /* editor setup */
8656   sei = setup.editor;
8657   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8658     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8659   setup.editor = sei;
8660
8661   /* shortcut setup */
8662   ssi = setup.shortcut;
8663   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8664     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8665   setup.shortcut = ssi;
8666
8667   /* player setup */
8668   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8669   {
8670     char prefix[30];
8671
8672     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8673
8674     sii = setup.input[pnr];
8675     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8676     {
8677       char full_token[100];
8678
8679       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8680       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8681                                 full_token);
8682     }
8683     setup.input[pnr] = sii;
8684   }
8685
8686   /* system setup */
8687   syi = setup.system;
8688   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8689     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8690   setup.system = syi;
8691
8692   /* internal setup */
8693   sxi = setup.internal;
8694   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8695     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8696   setup.internal = sxi;
8697
8698   /* debug setup */
8699   sdi = setup.debug;
8700   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8701     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8702   setup.debug = sdi;
8703
8704   /* options setup */
8705   soi = setup.options;
8706   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8707     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8708   setup.options = soi;
8709 }
8710
8711 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8712 {
8713   int i;
8714
8715   if (!setup_file_hash)
8716     return;
8717
8718   /* editor cascade setup */
8719   seci = setup.editor_cascade;
8720   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8721     setSetupInfo(editor_cascade_setup_tokens, i,
8722                  getHashEntry(setup_file_hash,
8723                               editor_cascade_setup_tokens[i].text));
8724   setup.editor_cascade = seci;
8725 }
8726
8727 void LoadSetupFromFilename(char *filename)
8728 {
8729   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8730
8731   if (setup_file_hash)
8732   {
8733     decodeSetupFileHash(setup_file_hash);
8734
8735     freeSetupFileHash(setup_file_hash);
8736   }
8737   else
8738   {
8739     Error(ERR_DEBUG, "using default setup values");
8740   }
8741 }
8742
8743 static void LoadSetup_SpecialPostProcessing()
8744 {
8745   char *player_name_new;
8746
8747   /* needed to work around problems with fixed length strings */
8748   player_name_new = get_corrected_login_name(setup.player_name);
8749   free(setup.player_name);
8750   setup.player_name = player_name_new;
8751
8752   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8753   if (setup.scroll_delay == FALSE)
8754   {
8755     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8756     setup.scroll_delay = TRUE;                  /* now always "on" */
8757   }
8758
8759   /* make sure that scroll delay value stays inside valid range */
8760   setup.scroll_delay_value =
8761     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8762 }
8763
8764 void LoadSetup()
8765 {
8766   char *filename;
8767
8768   /* always start with reliable default values */
8769   setSetupInfoToDefaults(&setup);
8770
8771   /* try to load setup values from default setup file */
8772   filename = getDefaultSetupFilename();
8773
8774   if (fileExists(filename))
8775     LoadSetupFromFilename(filename);
8776
8777   /* try to load setup values from user setup file */
8778   filename = getSetupFilename();
8779
8780   LoadSetupFromFilename(filename);
8781
8782   LoadSetup_SpecialPostProcessing();
8783 }
8784
8785 void LoadSetup_EditorCascade()
8786 {
8787   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8788   SetupFileHash *setup_file_hash = NULL;
8789
8790   /* always start with reliable default values */
8791   setSetupInfoToDefaults_EditorCascade(&setup);
8792
8793   setup_file_hash = loadSetupFileHash(filename);
8794
8795   if (setup_file_hash)
8796   {
8797     decodeSetupFileHash_EditorCascade(setup_file_hash);
8798
8799     freeSetupFileHash(setup_file_hash);
8800   }
8801
8802   free(filename);
8803 }
8804
8805 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8806                                            char *mapping_line)
8807 {
8808   char mapping_guid[MAX_LINE_LEN];
8809   char *mapping_start, *mapping_end;
8810
8811   // get GUID from game controller mapping line: copy complete line
8812   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8813   mapping_guid[MAX_LINE_LEN - 1] = '\0';
8814
8815   // get GUID from game controller mapping line: cut after GUID part
8816   mapping_start = strchr(mapping_guid, ',');
8817   if (mapping_start != NULL)
8818     *mapping_start = '\0';
8819
8820   // cut newline from game controller mapping line
8821   mapping_end = strchr(mapping_line, '\n');
8822   if (mapping_end != NULL)
8823     *mapping_end = '\0';
8824
8825   // add mapping entry to game controller mappings hash
8826   setHashEntry(mappings_hash, mapping_guid, mapping_line);
8827 }
8828
8829 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
8830                                                  char *filename)
8831 {
8832   FILE *file;
8833
8834   if (!(file = fopen(filename, MODE_READ)))
8835   {
8836     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
8837
8838     return;
8839   }
8840
8841   while (!feof(file))
8842   {
8843     char line[MAX_LINE_LEN];
8844
8845     if (!fgets(line, MAX_LINE_LEN, file))
8846       break;
8847
8848     addGameControllerMappingToHash(mappings_hash, line);
8849   }
8850
8851   fclose(file);
8852 }
8853
8854 void SaveSetup()
8855 {
8856   char *filename = getSetupFilename();
8857   FILE *file;
8858   int i, pnr;
8859
8860   InitUserDataDirectory();
8861
8862   if (!(file = fopen(filename, MODE_WRITE)))
8863   {
8864     Error(ERR_WARN, "cannot write setup file '%s'", filename);
8865     return;
8866   }
8867
8868   fprintFileHeader(file, SETUP_FILENAME);
8869
8870   /* global setup */
8871   si = setup;
8872   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8873   {
8874     /* just to make things nicer :) */
8875     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8876         i == SETUP_TOKEN_GRAPHICS_SET ||
8877         i == SETUP_TOKEN_VOLUME_SIMPLE ||
8878         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8879       fprintf(file, "\n");
8880
8881     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8882   }
8883
8884   /* editor setup */
8885   sei = setup.editor;
8886   fprintf(file, "\n");
8887   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8888     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8889
8890   /* shortcut setup */
8891   ssi = setup.shortcut;
8892   fprintf(file, "\n");
8893   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8894     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8895
8896   /* player setup */
8897   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8898   {
8899     char prefix[30];
8900
8901     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8902     fprintf(file, "\n");
8903
8904     sii = setup.input[pnr];
8905     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8906       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8907   }
8908
8909   /* system setup */
8910   syi = setup.system;
8911   fprintf(file, "\n");
8912   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8913     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8914
8915   /* internal setup */
8916   /* (internal setup values not saved to user setup file) */
8917
8918   /* debug setup */
8919   sdi = setup.debug;
8920   fprintf(file, "\n");
8921   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8922     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8923
8924   /* options setup */
8925   soi = setup.options;
8926   fprintf(file, "\n");
8927   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8928     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8929
8930   fclose(file);
8931
8932   SetFilePermissions(filename, PERMS_PRIVATE);
8933 }
8934
8935 void SaveSetup_EditorCascade()
8936 {
8937   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8938   FILE *file;
8939   int i;
8940
8941   InitUserDataDirectory();
8942
8943   if (!(file = fopen(filename, MODE_WRITE)))
8944   {
8945     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8946     free(filename);
8947     return;
8948   }
8949
8950   fprintFileHeader(file, EDITORCASCADE_FILENAME);
8951
8952   seci = setup.editor_cascade;
8953   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8954     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8955
8956   fclose(file);
8957
8958   SetFilePermissions(filename, PERMS_PRIVATE);
8959
8960   free(filename);
8961 }
8962
8963 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
8964                                                   char *filename)
8965 {
8966   FILE *file;
8967
8968   if (!(file = fopen(filename, MODE_WRITE)))
8969   {
8970     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
8971
8972     return;
8973   }
8974
8975   BEGIN_HASH_ITERATION(mappings_hash, itr)
8976   {
8977     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
8978   }
8979   END_HASH_ITERATION(mappings_hash, itr)
8980
8981   fclose(file);
8982 }
8983
8984 void SaveSetup_AddGameControllerMapping(char *mapping)
8985 {
8986   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
8987   SetupFileHash *mappings_hash = newSetupFileHash();
8988
8989   InitUserDataDirectory();
8990
8991   // load existing personal game controller mappings
8992   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
8993
8994   // add new mapping to personal game controller mappings
8995   addGameControllerMappingToHash(mappings_hash, mapping);
8996
8997   // save updated personal game controller mappings
8998   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
8999
9000   freeSetupFileHash(mappings_hash);
9001   free(filename);
9002 }
9003
9004 void LoadCustomElementDescriptions()
9005 {
9006   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9007   SetupFileHash *setup_file_hash;
9008   int i;
9009
9010   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9011   {
9012     if (element_info[i].custom_description != NULL)
9013     {
9014       free(element_info[i].custom_description);
9015       element_info[i].custom_description = NULL;
9016     }
9017   }
9018
9019   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9020     return;
9021
9022   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9023   {
9024     char *token = getStringCat2(element_info[i].token_name, ".name");
9025     char *value = getHashEntry(setup_file_hash, token);
9026
9027     if (value != NULL)
9028       element_info[i].custom_description = getStringCopy(value);
9029
9030     free(token);
9031   }
9032
9033   freeSetupFileHash(setup_file_hash);
9034 }
9035
9036 static int getElementFromToken(char *token)
9037 {
9038   char *value = getHashEntry(element_token_hash, token);
9039
9040   if (value != NULL)
9041     return atoi(value);
9042
9043   Error(ERR_WARN, "unknown element token '%s'", token);
9044
9045   return EL_UNDEFINED;
9046 }
9047
9048 static int get_token_parameter_value(char *token, char *value_raw)
9049 {
9050   char *suffix;
9051
9052   if (token == NULL || value_raw == NULL)
9053     return ARG_UNDEFINED_VALUE;
9054
9055   suffix = strrchr(token, '.');
9056   if (suffix == NULL)
9057     suffix = token;
9058
9059   if (strEqual(suffix, ".element"))
9060     return getElementFromToken(value_raw);
9061
9062   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9063   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9064 }
9065
9066 void InitMenuDesignSettings_Static()
9067 {
9068   int i;
9069
9070   /* always start with reliable default values from static default config */
9071   for (i = 0; image_config_vars[i].token != NULL; i++)
9072   {
9073     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9074
9075     if (value != NULL)
9076       *image_config_vars[i].value =
9077         get_token_parameter_value(image_config_vars[i].token, value);
9078   }
9079 }
9080
9081 static void InitMenuDesignSettings_SpecialPreProcessing()
9082 {
9083   int i;
9084
9085   /* the following initializes hierarchical values from static configuration */
9086
9087   /* special case: initialize "ARG_DEFAULT" values in static default config */
9088   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9089   titlescreen_initial_first_default.fade_mode  =
9090     title_initial_first_default.fade_mode;
9091   titlescreen_initial_first_default.fade_delay =
9092     title_initial_first_default.fade_delay;
9093   titlescreen_initial_first_default.post_delay =
9094     title_initial_first_default.post_delay;
9095   titlescreen_initial_first_default.auto_delay =
9096     title_initial_first_default.auto_delay;
9097   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9098   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9099   titlescreen_first_default.post_delay = title_first_default.post_delay;
9100   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9101   titlemessage_initial_first_default.fade_mode  =
9102     title_initial_first_default.fade_mode;
9103   titlemessage_initial_first_default.fade_delay =
9104     title_initial_first_default.fade_delay;
9105   titlemessage_initial_first_default.post_delay =
9106     title_initial_first_default.post_delay;
9107   titlemessage_initial_first_default.auto_delay =
9108     title_initial_first_default.auto_delay;
9109   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9110   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9111   titlemessage_first_default.post_delay = title_first_default.post_delay;
9112   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9113
9114   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9115   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9116   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9117   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9118   titlescreen_default.fade_mode  = title_default.fade_mode;
9119   titlescreen_default.fade_delay = title_default.fade_delay;
9120   titlescreen_default.post_delay = title_default.post_delay;
9121   titlescreen_default.auto_delay = title_default.auto_delay;
9122   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9123   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9124   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9125   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9126   titlemessage_default.fade_mode  = title_default.fade_mode;
9127   titlemessage_default.fade_delay = title_default.fade_delay;
9128   titlemessage_default.post_delay = title_default.post_delay;
9129   titlemessage_default.auto_delay = title_default.auto_delay;
9130
9131   /* special case: initialize "ARG_DEFAULT" values in static default config */
9132   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9133   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9134   {
9135     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9136     titlescreen_first[i] = titlescreen_first_default;
9137     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9138     titlemessage_first[i] = titlemessage_first_default;
9139
9140     titlescreen_initial[i] = titlescreen_initial_default;
9141     titlescreen[i] = titlescreen_default;
9142     titlemessage_initial[i] = titlemessage_initial_default;
9143     titlemessage[i] = titlemessage_default;
9144   }
9145
9146   /* special case: initialize "ARG_DEFAULT" values in static default config */
9147   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9148   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9149   {
9150     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9151       continue;
9152
9153     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9154     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9155     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9156   }
9157
9158   /* special case: initialize "ARG_DEFAULT" values in static default config */
9159   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9160   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9161   {
9162     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9163     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9164     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9165
9166     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9167       continue;
9168
9169     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9170   }
9171 }
9172
9173 static void InitMenuDesignSettings_SpecialPostProcessing()
9174 {
9175   static struct
9176   {
9177     struct XY *dst, *src;
9178   }
9179   game_buttons_xy[] =
9180   {
9181     { &game.button.save,        &game.button.stop       },
9182     { &game.button.pause2,      &game.button.pause      },
9183     { &game.button.load,        &game.button.play       },
9184     { &game.button.undo,        &game.button.stop       },
9185     { &game.button.redo,        &game.button.play       },
9186
9187     { NULL,                     NULL                    }
9188   };
9189   int i;
9190
9191   /* special case: initialize later added SETUP list size from LEVELS value */
9192   if (menu.list_size[GAME_MODE_SETUP] == -1)
9193     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9194
9195   /* set default position for snapshot buttons to stop/pause/play buttons */
9196   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9197     if ((*game_buttons_xy[i].dst).x == -1 &&
9198         (*game_buttons_xy[i].dst).y == -1)
9199       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9200 }
9201
9202 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9203 {
9204   static struct
9205   {
9206     struct XYTileSize *dst, *src;
9207     int graphic;
9208   }
9209   editor_buttons_xy[] =
9210   {
9211     {
9212       &editor.button.element_left,      &editor.palette.element_left,
9213       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9214     },
9215     {
9216       &editor.button.element_middle,    &editor.palette.element_middle,
9217       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9218     },
9219     {
9220       &editor.button.element_right,     &editor.palette.element_right,
9221       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9222     },
9223
9224     { NULL,                     NULL                    }
9225   };
9226   int i;
9227
9228   /* set default position for element buttons to element graphics */
9229   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9230   {
9231     if ((*editor_buttons_xy[i].dst).x == -1 &&
9232         (*editor_buttons_xy[i].dst).y == -1)
9233     {
9234       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9235
9236       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9237
9238       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9239     }
9240   }
9241 }
9242
9243 static void LoadMenuDesignSettingsFromFilename(char *filename)
9244 {
9245   static struct TitleFadingInfo tfi;
9246   static struct TitleMessageInfo tmi;
9247   static struct TokenInfo title_tokens[] =
9248   {
9249     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9250     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9251     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9252     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9253
9254     { -1,               NULL,                   NULL                    }
9255   };
9256   static struct TokenInfo titlemessage_tokens[] =
9257   {
9258     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9259     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9260     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9261     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9262     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9263     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9264     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9265     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9266     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9267     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9268     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9269     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9270     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9271     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9272     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9273     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9274     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9275
9276     { -1,               NULL,                   NULL                    }
9277   };
9278   static struct
9279   {
9280     struct TitleFadingInfo *info;
9281     char *text;
9282   }
9283   title_info[] =
9284   {
9285     /* initialize first titles from "enter screen" definitions, if defined */
9286     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9287     { &title_first_default,             "menu.enter_screen.TITLE"       },
9288
9289     /* initialize title screens from "next screen" definitions, if defined */
9290     { &title_initial_default,           "menu.next_screen.TITLE"        },
9291     { &title_default,                   "menu.next_screen.TITLE"        },
9292
9293     { NULL,                             NULL                            }
9294   };
9295   static struct
9296   {
9297     struct TitleMessageInfo *array;
9298     char *text;
9299   }
9300   titlemessage_arrays[] =
9301   {
9302     /* initialize first titles from "enter screen" definitions, if defined */
9303     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9304     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9305     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9306     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9307
9308     /* initialize titles from "next screen" definitions, if defined */
9309     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9310     { titlescreen,                      "menu.next_screen.TITLE"        },
9311     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9312     { titlemessage,                     "menu.next_screen.TITLE"        },
9313
9314     /* overwrite titles with title definitions, if defined */
9315     { titlescreen_initial_first,        "[title_initial]"               },
9316     { titlescreen_first,                "[title]"                       },
9317     { titlemessage_initial_first,       "[title_initial]"               },
9318     { titlemessage_first,               "[title]"                       },
9319
9320     { titlescreen_initial,              "[title_initial]"               },
9321     { titlescreen,                      "[title]"                       },
9322     { titlemessage_initial,             "[title_initial]"               },
9323     { titlemessage,                     "[title]"                       },
9324
9325     /* overwrite titles with title screen/message definitions, if defined */
9326     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9327     { titlescreen_first,                "[titlescreen]"                 },
9328     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9329     { titlemessage_first,               "[titlemessage]"                },
9330
9331     { titlescreen_initial,              "[titlescreen_initial]"         },
9332     { titlescreen,                      "[titlescreen]"                 },
9333     { titlemessage_initial,             "[titlemessage_initial]"        },
9334     { titlemessage,                     "[titlemessage]"                },
9335
9336     { NULL,                             NULL                            }
9337   };
9338   SetupFileHash *setup_file_hash;
9339   int i, j, k;
9340
9341   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9342     return;
9343
9344   /* the following initializes hierarchical values from dynamic configuration */
9345
9346   /* special case: initialize with default values that may be overwritten */
9347   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9348   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9349   {
9350     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9351     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9352     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9353
9354     if (value_1 != NULL)
9355       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9356     if (value_2 != NULL)
9357       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9358     if (value_3 != NULL)
9359       menu.list_size[i] = get_integer_from_string(value_3);
9360   }
9361
9362   /* special case: initialize with default values that may be overwritten */
9363   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9364   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9365   {
9366     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9367     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9368
9369     if (value_1 != NULL)
9370       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9371     if (value_2 != NULL)
9372       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9373
9374     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9375     {
9376       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9377
9378       if (value_1 != NULL)
9379         menu.list_size_info[i] = get_integer_from_string(value_1);
9380     }
9381   }
9382
9383   /* special case: initialize with default values that may be overwritten */
9384   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9385   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9386   {
9387     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9388     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9389
9390     if (value_1 != NULL)
9391       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9392     if (value_2 != NULL)
9393       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9394   }
9395
9396   /* special case: initialize with default values that may be overwritten */
9397   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9398   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9399   {
9400     char *token_1 = "menu.enter_screen.fade_mode";
9401     char *token_2 = "menu.enter_screen.fade_delay";
9402     char *token_3 = "menu.enter_screen.post_delay";
9403     char *token_4 = "menu.leave_screen.fade_mode";
9404     char *token_5 = "menu.leave_screen.fade_delay";
9405     char *token_6 = "menu.leave_screen.post_delay";
9406     char *token_7 = "menu.next_screen.fade_mode";
9407     char *token_8 = "menu.next_screen.fade_delay";
9408     char *token_9 = "menu.next_screen.post_delay";
9409     char *value_1 = getHashEntry(setup_file_hash, token_1);
9410     char *value_2 = getHashEntry(setup_file_hash, token_2);
9411     char *value_3 = getHashEntry(setup_file_hash, token_3);
9412     char *value_4 = getHashEntry(setup_file_hash, token_4);
9413     char *value_5 = getHashEntry(setup_file_hash, token_5);
9414     char *value_6 = getHashEntry(setup_file_hash, token_6);
9415     char *value_7 = getHashEntry(setup_file_hash, token_7);
9416     char *value_8 = getHashEntry(setup_file_hash, token_8);
9417     char *value_9 = getHashEntry(setup_file_hash, token_9);
9418
9419     if (value_1 != NULL)
9420       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9421                                                                  value_1);
9422     if (value_2 != NULL)
9423       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9424                                                                   value_2);
9425     if (value_3 != NULL)
9426       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9427                                                                   value_3);
9428     if (value_4 != NULL)
9429       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9430                                                                  value_4);
9431     if (value_5 != NULL)
9432       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9433                                                                   value_5);
9434     if (value_6 != NULL)
9435       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9436                                                                   value_6);
9437     if (value_7 != NULL)
9438       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9439                                                                 value_7);
9440     if (value_8 != NULL)
9441       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9442                                                                  value_8);
9443     if (value_9 != NULL)
9444       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9445                                                                  value_9);
9446   }
9447
9448   /* special case: initialize with default values that may be overwritten */
9449   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9450   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9451   {
9452     char *token_w1 = "viewport.window.width";
9453     char *token_w2 = "viewport.window.height";
9454     char *token_01 = "viewport.playfield.x";
9455     char *token_02 = "viewport.playfield.y";
9456     char *token_03 = "viewport.playfield.width";
9457     char *token_04 = "viewport.playfield.height";
9458     char *token_05 = "viewport.playfield.border_size";
9459     char *token_06 = "viewport.door_1.x";
9460     char *token_07 = "viewport.door_1.y";
9461     char *token_08 = "viewport.door_1.width";
9462     char *token_09 = "viewport.door_1.height";
9463     char *token_10 = "viewport.door_1.border_size";
9464     char *token_11 = "viewport.door_2.x";
9465     char *token_12 = "viewport.door_2.y";
9466     char *token_13 = "viewport.door_2.width";
9467     char *token_14 = "viewport.door_2.height";
9468     char *token_15 = "viewport.door_2.border_size";
9469     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9470     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9471     char *value_01 = getHashEntry(setup_file_hash, token_01);
9472     char *value_02 = getHashEntry(setup_file_hash, token_02);
9473     char *value_03 = getHashEntry(setup_file_hash, token_03);
9474     char *value_04 = getHashEntry(setup_file_hash, token_04);
9475     char *value_05 = getHashEntry(setup_file_hash, token_05);
9476     char *value_06 = getHashEntry(setup_file_hash, token_06);
9477     char *value_07 = getHashEntry(setup_file_hash, token_07);
9478     char *value_08 = getHashEntry(setup_file_hash, token_08);
9479     char *value_09 = getHashEntry(setup_file_hash, token_09);
9480     char *value_10 = getHashEntry(setup_file_hash, token_10);
9481     char *value_11 = getHashEntry(setup_file_hash, token_11);
9482     char *value_12 = getHashEntry(setup_file_hash, token_12);
9483     char *value_13 = getHashEntry(setup_file_hash, token_13);
9484     char *value_14 = getHashEntry(setup_file_hash, token_14);
9485     char *value_15 = getHashEntry(setup_file_hash, token_15);
9486
9487     if (value_w1 != NULL)
9488       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9489     if (value_w2 != NULL)
9490       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9491     if (value_01 != NULL)
9492       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9493     if (value_02 != NULL)
9494       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9495     if (value_03 != NULL)
9496       viewport.playfield[i].width = get_token_parameter_value(token_03,
9497                                                               value_03);
9498     if (value_04 != NULL)
9499       viewport.playfield[i].height = get_token_parameter_value(token_04,
9500                                                                value_04);
9501     if (value_05 != NULL)
9502       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9503                                                                     value_05);
9504     if (value_06 != NULL)
9505       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9506     if (value_07 != NULL)
9507       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9508     if (value_08 != NULL)
9509       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9510     if (value_09 != NULL)
9511       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9512     if (value_10 != NULL)
9513       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9514                                                                  value_10);
9515     if (value_11 != NULL)
9516       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9517     if (value_12 != NULL)
9518       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9519     if (value_13 != NULL)
9520       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9521     if (value_14 != NULL)
9522       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9523     if (value_15 != NULL)
9524       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9525                                                                  value_15);
9526   }
9527
9528   /* special case: initialize with default values that may be overwritten */
9529   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9530   for (i = 0; title_info[i].info != NULL; i++)
9531   {
9532     struct TitleFadingInfo *info = title_info[i].info;
9533     char *base_token = title_info[i].text;
9534
9535     for (j = 0; title_tokens[j].type != -1; j++)
9536     {
9537       char *token = getStringCat2(base_token, title_tokens[j].text);
9538       char *value = getHashEntry(setup_file_hash, token);
9539
9540       if (value != NULL)
9541       {
9542         int parameter_value = get_token_parameter_value(token, value);
9543
9544         tfi = *info;
9545
9546         *(int *)title_tokens[j].value = (int)parameter_value;
9547
9548         *info = tfi;
9549       }
9550
9551       free(token);
9552     }
9553   }
9554
9555   /* special case: initialize with default values that may be overwritten */
9556   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9557   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9558   {
9559     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9560     char *base_token = titlemessage_arrays[i].text;
9561
9562     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9563     {
9564       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9565       char *value = getHashEntry(setup_file_hash, token);
9566
9567       if (value != NULL)
9568       {
9569         int parameter_value = get_token_parameter_value(token, value);
9570
9571         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9572         {
9573           tmi = array[k];
9574
9575           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9576             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9577           else
9578             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9579
9580           array[k] = tmi;
9581         }
9582       }
9583
9584       free(token);
9585     }
9586   }
9587
9588   /* read (and overwrite with) values that may be specified in config file */
9589   for (i = 0; image_config_vars[i].token != NULL; i++)
9590   {
9591     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9592
9593     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9594     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9595       *image_config_vars[i].value =
9596         get_token_parameter_value(image_config_vars[i].token, value);
9597   }
9598
9599   freeSetupFileHash(setup_file_hash);
9600 }
9601
9602 void LoadMenuDesignSettings()
9603 {
9604   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9605
9606   InitMenuDesignSettings_Static();
9607   InitMenuDesignSettings_SpecialPreProcessing();
9608
9609   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9610   {
9611     /* first look for special settings configured in level series config */
9612     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9613
9614     if (fileExists(filename_base))
9615       LoadMenuDesignSettingsFromFilename(filename_base);
9616   }
9617
9618   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9619
9620   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9621     LoadMenuDesignSettingsFromFilename(filename_local);
9622
9623   InitMenuDesignSettings_SpecialPostProcessing();
9624 }
9625
9626 void LoadMenuDesignSettings_AfterGraphics()
9627 {
9628   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9629 }
9630
9631 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9632 {
9633   char *filename = getEditorSetupFilename();
9634   SetupFileList *setup_file_list, *list;
9635   SetupFileHash *element_hash;
9636   int num_unknown_tokens = 0;
9637   int i;
9638
9639   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9640     return;
9641
9642   element_hash = newSetupFileHash();
9643
9644   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9645     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9646
9647   /* determined size may be larger than needed (due to unknown elements) */
9648   *num_elements = 0;
9649   for (list = setup_file_list; list != NULL; list = list->next)
9650     (*num_elements)++;
9651
9652   /* add space for up to 3 more elements for padding that may be needed */
9653   *num_elements += 3;
9654
9655   /* free memory for old list of elements, if needed */
9656   checked_free(*elements);
9657
9658   /* allocate memory for new list of elements */
9659   *elements = checked_malloc(*num_elements * sizeof(int));
9660
9661   *num_elements = 0;
9662   for (list = setup_file_list; list != NULL; list = list->next)
9663   {
9664     char *value = getHashEntry(element_hash, list->token);
9665
9666     if (value == NULL)          /* try to find obsolete token mapping */
9667     {
9668       char *mapped_token = get_mapped_token(list->token);
9669
9670       if (mapped_token != NULL)
9671       {
9672         value = getHashEntry(element_hash, mapped_token);
9673
9674         free(mapped_token);
9675       }
9676     }
9677
9678     if (value != NULL)
9679     {
9680       (*elements)[(*num_elements)++] = atoi(value);
9681     }
9682     else
9683     {
9684       if (num_unknown_tokens == 0)
9685       {
9686         Error(ERR_INFO_LINE, "-");
9687         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9688         Error(ERR_INFO, "- config file: '%s'", filename);
9689
9690         num_unknown_tokens++;
9691       }
9692
9693       Error(ERR_INFO, "- token: '%s'", list->token);
9694     }
9695   }
9696
9697   if (num_unknown_tokens > 0)
9698     Error(ERR_INFO_LINE, "-");
9699
9700   while (*num_elements % 4)     /* pad with empty elements, if needed */
9701     (*elements)[(*num_elements)++] = EL_EMPTY;
9702
9703   freeSetupFileList(setup_file_list);
9704   freeSetupFileHash(element_hash);
9705
9706 #if 0
9707   for (i = 0; i < *num_elements; i++)
9708     printf("editor: element '%s' [%d]\n",
9709            element_info[(*elements)[i]].token_name, (*elements)[i]);
9710 #endif
9711 }
9712
9713 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9714                                                      boolean is_sound)
9715 {
9716   SetupFileHash *setup_file_hash = NULL;
9717   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9718   char *filename_music, *filename_prefix, *filename_info;
9719   struct
9720   {
9721     char *token;
9722     char **value_ptr;
9723   }
9724   token_to_value_ptr[] =
9725   {
9726     { "title_header",   &tmp_music_file_info.title_header       },
9727     { "artist_header",  &tmp_music_file_info.artist_header      },
9728     { "album_header",   &tmp_music_file_info.album_header       },
9729     { "year_header",    &tmp_music_file_info.year_header        },
9730
9731     { "title",          &tmp_music_file_info.title              },
9732     { "artist",         &tmp_music_file_info.artist             },
9733     { "album",          &tmp_music_file_info.album              },
9734     { "year",           &tmp_music_file_info.year               },
9735
9736     { NULL,             NULL                                    },
9737   };
9738   int i;
9739
9740   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9741                     getCustomMusicFilename(basename));
9742
9743   if (filename_music == NULL)
9744     return NULL;
9745
9746   /* ---------- try to replace file extension ---------- */
9747
9748   filename_prefix = getStringCopy(filename_music);
9749   if (strrchr(filename_prefix, '.') != NULL)
9750     *strrchr(filename_prefix, '.') = '\0';
9751   filename_info = getStringCat2(filename_prefix, ".txt");
9752
9753   if (fileExists(filename_info))
9754     setup_file_hash = loadSetupFileHash(filename_info);
9755
9756   free(filename_prefix);
9757   free(filename_info);
9758
9759   if (setup_file_hash == NULL)
9760   {
9761     /* ---------- try to add file extension ---------- */
9762
9763     filename_prefix = getStringCopy(filename_music);
9764     filename_info = getStringCat2(filename_prefix, ".txt");
9765
9766     if (fileExists(filename_info))
9767       setup_file_hash = loadSetupFileHash(filename_info);
9768
9769     free(filename_prefix);
9770     free(filename_info);
9771   }
9772
9773   if (setup_file_hash == NULL)
9774     return NULL;
9775
9776   /* ---------- music file info found ---------- */
9777
9778   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9779
9780   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9781   {
9782     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9783
9784     *token_to_value_ptr[i].value_ptr =
9785       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9786   }
9787
9788   tmp_music_file_info.basename = getStringCopy(basename);
9789   tmp_music_file_info.music = music;
9790   tmp_music_file_info.is_sound = is_sound;
9791
9792   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9793   *new_music_file_info = tmp_music_file_info;
9794
9795   return new_music_file_info;
9796 }
9797
9798 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9799 {
9800   return get_music_file_info_ext(basename, music, FALSE);
9801 }
9802
9803 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9804 {
9805   return get_music_file_info_ext(basename, sound, TRUE);
9806 }
9807
9808 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9809                                      char *basename, boolean is_sound)
9810 {
9811   for (; list != NULL; list = list->next)
9812     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9813       return TRUE;
9814
9815   return FALSE;
9816 }
9817
9818 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9819 {
9820   return music_info_listed_ext(list, basename, FALSE);
9821 }
9822
9823 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9824 {
9825   return music_info_listed_ext(list, basename, TRUE);
9826 }
9827
9828 void LoadMusicInfo()
9829 {
9830   char *music_directory = getCustomMusicDirectory();
9831   int num_music = getMusicListSize();
9832   int num_music_noconf = 0;
9833   int num_sounds = getSoundListSize();
9834   Directory *dir;
9835   DirectoryEntry *dir_entry;
9836   struct FileInfo *music, *sound;
9837   struct MusicFileInfo *next, **new;
9838   int i;
9839
9840   while (music_file_info != NULL)
9841   {
9842     next = music_file_info->next;
9843
9844     checked_free(music_file_info->basename);
9845
9846     checked_free(music_file_info->title_header);
9847     checked_free(music_file_info->artist_header);
9848     checked_free(music_file_info->album_header);
9849     checked_free(music_file_info->year_header);
9850
9851     checked_free(music_file_info->title);
9852     checked_free(music_file_info->artist);
9853     checked_free(music_file_info->album);
9854     checked_free(music_file_info->year);
9855
9856     free(music_file_info);
9857
9858     music_file_info = next;
9859   }
9860
9861   new = &music_file_info;
9862
9863   for (i = 0; i < num_music; i++)
9864   {
9865     music = getMusicListEntry(i);
9866
9867     if (music->filename == NULL)
9868       continue;
9869
9870     if (strEqual(music->filename, UNDEFINED_FILENAME))
9871       continue;
9872
9873     /* a configured file may be not recognized as music */
9874     if (!FileIsMusic(music->filename))
9875       continue;
9876
9877     if (!music_info_listed(music_file_info, music->filename))
9878     {
9879       *new = get_music_file_info(music->filename, i);
9880
9881       if (*new != NULL)
9882         new = &(*new)->next;
9883     }
9884   }
9885
9886   if ((dir = openDirectory(music_directory)) == NULL)
9887   {
9888     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9889     return;
9890   }
9891
9892   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
9893   {
9894     char *basename = dir_entry->basename;
9895     boolean music_already_used = FALSE;
9896     int i;
9897
9898     /* skip all music files that are configured in music config file */
9899     for (i = 0; i < num_music; i++)
9900     {
9901       music = getMusicListEntry(i);
9902
9903       if (music->filename == NULL)
9904         continue;
9905
9906       if (strEqual(basename, music->filename))
9907       {
9908         music_already_used = TRUE;
9909         break;
9910       }
9911     }
9912
9913     if (music_already_used)
9914       continue;
9915
9916     if (!FileIsMusic(dir_entry->filename))
9917       continue;
9918
9919     if (!music_info_listed(music_file_info, basename))
9920     {
9921       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9922
9923       if (*new != NULL)
9924         new = &(*new)->next;
9925     }
9926
9927     num_music_noconf++;
9928   }
9929
9930   closeDirectory(dir);
9931
9932   for (i = 0; i < num_sounds; i++)
9933   {
9934     sound = getSoundListEntry(i);
9935
9936     if (sound->filename == NULL)
9937       continue;
9938
9939     if (strEqual(sound->filename, UNDEFINED_FILENAME))
9940       continue;
9941
9942     /* a configured file may be not recognized as sound */
9943     if (!FileIsSound(sound->filename))
9944       continue;
9945
9946     if (!sound_info_listed(music_file_info, sound->filename))
9947     {
9948       *new = get_sound_file_info(sound->filename, i);
9949       if (*new != NULL)
9950         new = &(*new)->next;
9951     }
9952   }
9953 }
9954
9955 void add_helpanim_entry(int element, int action, int direction, int delay,
9956                         int *num_list_entries)
9957 {
9958   struct HelpAnimInfo *new_list_entry;
9959   (*num_list_entries)++;
9960
9961   helpanim_info =
9962     checked_realloc(helpanim_info,
9963                     *num_list_entries * sizeof(struct HelpAnimInfo));
9964   new_list_entry = &helpanim_info[*num_list_entries - 1];
9965
9966   new_list_entry->element = element;
9967   new_list_entry->action = action;
9968   new_list_entry->direction = direction;
9969   new_list_entry->delay = delay;
9970 }
9971
9972 void print_unknown_token(char *filename, char *token, int token_nr)
9973 {
9974   if (token_nr == 0)
9975   {
9976     Error(ERR_INFO_LINE, "-");
9977     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9978     Error(ERR_INFO, "- config file: '%s'", filename);
9979   }
9980
9981   Error(ERR_INFO, "- token: '%s'", token);
9982 }
9983
9984 void print_unknown_token_end(int token_nr)
9985 {
9986   if (token_nr > 0)
9987     Error(ERR_INFO_LINE, "-");
9988 }
9989
9990 void LoadHelpAnimInfo()
9991 {
9992   char *filename = getHelpAnimFilename();
9993   SetupFileList *setup_file_list = NULL, *list;
9994   SetupFileHash *element_hash, *action_hash, *direction_hash;
9995   int num_list_entries = 0;
9996   int num_unknown_tokens = 0;
9997   int i;
9998
9999   if (fileExists(filename))
10000     setup_file_list = loadSetupFileList(filename);
10001
10002   if (setup_file_list == NULL)
10003   {
10004     /* use reliable default values from static configuration */
10005     SetupFileList *insert_ptr;
10006
10007     insert_ptr = setup_file_list =
10008       newSetupFileList(helpanim_config[0].token,
10009                        helpanim_config[0].value);
10010
10011     for (i = 1; helpanim_config[i].token; i++)
10012       insert_ptr = addListEntry(insert_ptr,
10013                                 helpanim_config[i].token,
10014                                 helpanim_config[i].value);
10015   }
10016
10017   element_hash   = newSetupFileHash();
10018   action_hash    = newSetupFileHash();
10019   direction_hash = newSetupFileHash();
10020
10021   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10022     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10023
10024   for (i = 0; i < NUM_ACTIONS; i++)
10025     setHashEntry(action_hash, element_action_info[i].suffix,
10026                  i_to_a(element_action_info[i].value));
10027
10028   /* do not store direction index (bit) here, but direction value! */
10029   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10030     setHashEntry(direction_hash, element_direction_info[i].suffix,
10031                  i_to_a(1 << element_direction_info[i].value));
10032
10033   for (list = setup_file_list; list != NULL; list = list->next)
10034   {
10035     char *element_token, *action_token, *direction_token;
10036     char *element_value, *action_value, *direction_value;
10037     int delay = atoi(list->value);
10038
10039     if (strEqual(list->token, "end"))
10040     {
10041       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10042
10043       continue;
10044     }
10045
10046     /* first try to break element into element/action/direction parts;
10047        if this does not work, also accept combined "element[.act][.dir]"
10048        elements (like "dynamite.active"), which are unique elements */
10049
10050     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10051     {
10052       element_value = getHashEntry(element_hash, list->token);
10053       if (element_value != NULL)        /* element found */
10054         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10055                            &num_list_entries);
10056       else
10057       {
10058         /* no further suffixes found -- this is not an element */
10059         print_unknown_token(filename, list->token, num_unknown_tokens++);
10060       }
10061
10062       continue;
10063     }
10064
10065     /* token has format "<prefix>.<something>" */
10066
10067     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10068     direction_token = action_token;             /* ... or direction */
10069
10070     element_token = getStringCopy(list->token);
10071     *strchr(element_token, '.') = '\0';
10072
10073     element_value = getHashEntry(element_hash, element_token);
10074
10075     if (element_value == NULL)          /* this is no element */
10076     {
10077       element_value = getHashEntry(element_hash, list->token);
10078       if (element_value != NULL)        /* combined element found */
10079         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10080                            &num_list_entries);
10081       else
10082         print_unknown_token(filename, list->token, num_unknown_tokens++);
10083
10084       free(element_token);
10085
10086       continue;
10087     }
10088
10089     action_value = getHashEntry(action_hash, action_token);
10090
10091     if (action_value != NULL)           /* action found */
10092     {
10093       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10094                     &num_list_entries);
10095
10096       free(element_token);
10097
10098       continue;
10099     }
10100
10101     direction_value = getHashEntry(direction_hash, direction_token);
10102
10103     if (direction_value != NULL)        /* direction found */
10104     {
10105       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10106                          &num_list_entries);
10107
10108       free(element_token);
10109
10110       continue;
10111     }
10112
10113     if (strchr(action_token + 1, '.') == NULL)
10114     {
10115       /* no further suffixes found -- this is not an action nor direction */
10116
10117       element_value = getHashEntry(element_hash, list->token);
10118       if (element_value != NULL)        /* combined element found */
10119         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10120                            &num_list_entries);
10121       else
10122         print_unknown_token(filename, list->token, num_unknown_tokens++);
10123
10124       free(element_token);
10125
10126       continue;
10127     }
10128
10129     /* token has format "<prefix>.<suffix>.<something>" */
10130
10131     direction_token = strchr(action_token + 1, '.');
10132
10133     action_token = getStringCopy(action_token);
10134     *strchr(action_token + 1, '.') = '\0';
10135
10136     action_value = getHashEntry(action_hash, action_token);
10137
10138     if (action_value == NULL)           /* this is no action */
10139     {
10140       element_value = getHashEntry(element_hash, list->token);
10141       if (element_value != NULL)        /* combined element found */
10142         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10143                            &num_list_entries);
10144       else
10145         print_unknown_token(filename, list->token, num_unknown_tokens++);
10146
10147       free(element_token);
10148       free(action_token);
10149
10150       continue;
10151     }
10152
10153     direction_value = getHashEntry(direction_hash, direction_token);
10154
10155     if (direction_value != NULL)        /* direction found */
10156     {
10157       add_helpanim_entry(atoi(element_value), atoi(action_value),
10158                          atoi(direction_value), delay, &num_list_entries);
10159
10160       free(element_token);
10161       free(action_token);
10162
10163       continue;
10164     }
10165
10166     /* this is no direction */
10167
10168     element_value = getHashEntry(element_hash, list->token);
10169     if (element_value != NULL)          /* combined element found */
10170       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10171                          &num_list_entries);
10172     else
10173       print_unknown_token(filename, list->token, num_unknown_tokens++);
10174
10175     free(element_token);
10176     free(action_token);
10177   }
10178
10179   print_unknown_token_end(num_unknown_tokens);
10180
10181   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10182   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10183
10184   freeSetupFileList(setup_file_list);
10185   freeSetupFileHash(element_hash);
10186   freeSetupFileHash(action_hash);
10187   freeSetupFileHash(direction_hash);
10188
10189 #if 0
10190   for (i = 0; i < num_list_entries; i++)
10191     printf("::: '%s': %d, %d, %d => %d\n",
10192            EL_NAME(helpanim_info[i].element),
10193            helpanim_info[i].element,
10194            helpanim_info[i].action,
10195            helpanim_info[i].direction,
10196            helpanim_info[i].delay);
10197 #endif
10198 }
10199
10200 void LoadHelpTextInfo()
10201 {
10202   char *filename = getHelpTextFilename();
10203   int i;
10204
10205   if (helptext_info != NULL)
10206   {
10207     freeSetupFileHash(helptext_info);
10208     helptext_info = NULL;
10209   }
10210
10211   if (fileExists(filename))
10212     helptext_info = loadSetupFileHash(filename);
10213
10214   if (helptext_info == NULL)
10215   {
10216     /* use reliable default values from static configuration */
10217     helptext_info = newSetupFileHash();
10218
10219     for (i = 0; helptext_config[i].token; i++)
10220       setHashEntry(helptext_info,
10221                    helptext_config[i].token,
10222                    helptext_config[i].value);
10223   }
10224
10225 #if 0
10226   BEGIN_HASH_ITERATION(helptext_info, itr)
10227   {
10228     printf("::: '%s' => '%s'\n",
10229            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10230   }
10231   END_HASH_ITERATION(hash, itr)
10232 #endif
10233 }
10234
10235
10236 /* ------------------------------------------------------------------------- */
10237 /* convert levels                                                            */
10238 /* ------------------------------------------------------------------------- */
10239
10240 #define MAX_NUM_CONVERT_LEVELS          1000
10241
10242 void ConvertLevels()
10243 {
10244   static LevelDirTree *convert_leveldir = NULL;
10245   static int convert_level_nr = -1;
10246   static int num_levels_handled = 0;
10247   static int num_levels_converted = 0;
10248   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10249   int i;
10250
10251   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10252                                                global.convert_leveldir);
10253
10254   if (convert_leveldir == NULL)
10255     Error(ERR_EXIT, "no such level identifier: '%s'",
10256           global.convert_leveldir);
10257
10258   leveldir_current = convert_leveldir;
10259
10260   if (global.convert_level_nr != -1)
10261   {
10262     convert_leveldir->first_level = global.convert_level_nr;
10263     convert_leveldir->last_level  = global.convert_level_nr;
10264   }
10265
10266   convert_level_nr = convert_leveldir->first_level;
10267
10268   PrintLine("=", 79);
10269   Print("Converting levels\n");
10270   PrintLine("-", 79);
10271   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10272   Print("Level series name:       '%s'\n", convert_leveldir->name);
10273   Print("Level series author:     '%s'\n", convert_leveldir->author);
10274   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10275   PrintLine("=", 79);
10276   Print("\n");
10277
10278   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10279     levels_failed[i] = FALSE;
10280
10281   while (convert_level_nr <= convert_leveldir->last_level)
10282   {
10283     char *level_filename;
10284     boolean new_level;
10285
10286     level_nr = convert_level_nr++;
10287
10288     Print("Level %03d: ", level_nr);
10289
10290     LoadLevel(level_nr);
10291     if (level.no_level_file || level.no_valid_file)
10292     {
10293       Print("(no level)\n");
10294       continue;
10295     }
10296
10297     Print("converting level ... ");
10298
10299     level_filename = getDefaultLevelFilename(level_nr);
10300     new_level = !fileExists(level_filename);
10301
10302     if (new_level)
10303     {
10304       SaveLevel(level_nr);
10305
10306       num_levels_converted++;
10307
10308       Print("converted.\n");
10309     }
10310     else
10311     {
10312       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10313         levels_failed[level_nr] = TRUE;
10314
10315       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10316     }
10317
10318     num_levels_handled++;
10319   }
10320
10321   Print("\n");
10322   PrintLine("=", 79);
10323   Print("Number of levels handled: %d\n", num_levels_handled);
10324   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10325          (num_levels_handled ?
10326           num_levels_converted * 100 / num_levels_handled : 0));
10327   PrintLine("-", 79);
10328   Print("Summary (for automatic parsing by scripts):\n");
10329   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10330          convert_leveldir->identifier, num_levels_converted,
10331          num_levels_handled,
10332          (num_levels_handled ?
10333           num_levels_converted * 100 / num_levels_handled : 0));
10334
10335   if (num_levels_handled != num_levels_converted)
10336   {
10337     Print(", FAILED:");
10338     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10339       if (levels_failed[i])
10340         Print(" %03d", i);
10341   }
10342
10343   Print("\n");
10344   PrintLine("=", 79);
10345
10346   CloseAllAndExit(0);
10347 }
10348
10349
10350 /* ------------------------------------------------------------------------- */
10351 /* create and save images for use in level sketches (raw BMP format)         */
10352 /* ------------------------------------------------------------------------- */
10353
10354 void CreateLevelSketchImages()
10355 {
10356 #if defined(TARGET_SDL)
10357   Bitmap *bitmap1;
10358   Bitmap *bitmap2;
10359   int i;
10360
10361   InitElementPropertiesGfxElement();
10362
10363   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10364   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10365
10366   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10367   {
10368     Bitmap *src_bitmap;
10369     int src_x, src_y;
10370     int element = getMappedElement(i);
10371     int graphic = el2edimg(element);
10372     char basename1[16];
10373     char basename2[16];
10374     char *filename1;
10375     char *filename2;
10376
10377     sprintf(basename1, "%03d.bmp", i);
10378     sprintf(basename2, "%03ds.bmp", i);
10379
10380     filename1 = getPath2(global.create_images_dir, basename1);
10381     filename2 = getPath2(global.create_images_dir, basename2);
10382
10383     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10384     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10385                0, 0);
10386
10387     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10388       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10389
10390     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10391     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10392
10393     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10394       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10395
10396     free(filename1);
10397     free(filename2);
10398
10399     if (options.debug)
10400       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10401   }
10402
10403   FreeBitmap(bitmap1);
10404   FreeBitmap(bitmap2);
10405
10406   if (options.debug)
10407     printf("\n");
10408
10409   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10410
10411   CloseAllAndExit(0);
10412 #endif
10413 }
10414
10415
10416 /* ------------------------------------------------------------------------- */
10417 /* create and save images for custom and group elements (raw BMP format)     */
10418 /* ------------------------------------------------------------------------- */
10419
10420 void CreateCustomElementImages(char *directory)
10421 {
10422 #if defined(TARGET_SDL)
10423   char *src_basename = "RocksCE-template.ilbm";
10424   char *dst_basename = "RocksCE.bmp";
10425   char *src_filename = getPath2(directory, src_basename);
10426   char *dst_filename = getPath2(directory, dst_basename);
10427   Bitmap *src_bitmap;
10428   Bitmap *bitmap;
10429   int yoffset_ce = 0;
10430   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10431   int i;
10432
10433   SDLInitVideoDisplay();
10434
10435   ReCreateBitmap(&backbuffer, video.width, video.height);
10436
10437   src_bitmap = LoadImage(src_filename);
10438
10439   bitmap = CreateBitmap(TILEX * 16 * 2,
10440                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10441                         DEFAULT_DEPTH);
10442
10443   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10444   {
10445     int x = i % 16;
10446     int y = i / 16;
10447     int ii = i + 1;
10448     int j;
10449
10450     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10451                TILEX * x, TILEY * y + yoffset_ce);
10452
10453     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10454                TILEX, TILEY,
10455                TILEX * x + TILEX * 16,
10456                TILEY * y + yoffset_ce);
10457
10458     for (j = 2; j >= 0; j--)
10459     {
10460       int c = ii % 10;
10461
10462       BlitBitmap(src_bitmap, bitmap,
10463                  TILEX + c * 7, 0, 6, 10,
10464                  TILEX * x + 6 + j * 7,
10465                  TILEY * y + 11 + yoffset_ce);
10466
10467       BlitBitmap(src_bitmap, bitmap,
10468                  TILEX + c * 8, TILEY, 6, 10,
10469                  TILEX * 16 + TILEX * x + 6 + j * 8,
10470                  TILEY * y + 10 + yoffset_ce);
10471
10472       ii /= 10;
10473     }
10474   }
10475
10476   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10477   {
10478     int x = i % 16;
10479     int y = i / 16;
10480     int ii = i + 1;
10481     int j;
10482
10483     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10484                TILEX * x, TILEY * y + yoffset_ge);
10485
10486     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10487                TILEX, TILEY,
10488                TILEX * x + TILEX * 16,
10489                TILEY * y + yoffset_ge);
10490
10491     for (j = 1; j >= 0; j--)
10492     {
10493       int c = ii % 10;
10494
10495       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10496                  TILEX * x + 6 + j * 10,
10497                  TILEY * y + 11 + yoffset_ge);
10498
10499       BlitBitmap(src_bitmap, bitmap,
10500                  TILEX + c * 8, TILEY + 12, 6, 10,
10501                  TILEX * 16 + TILEX * x + 10 + j * 8,
10502                  TILEY * y + 10 + yoffset_ge);
10503
10504       ii /= 10;
10505     }
10506   }
10507
10508   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10509     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10510
10511   FreeBitmap(bitmap);
10512
10513   CloseAllAndExit(0);
10514 #endif
10515 }