757e32a3e63cc821ab87db56f7b010be0b57230b
[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   int i, j, x, y;
3668
3669   level->fieldx = level_sp->width;
3670   level->fieldy = level_sp->height;
3671
3672   for (x = 0; x < level->fieldx; x++)
3673   {
3674     for (y = 0; y < level->fieldy; y++)
3675     {
3676       int element_old = level_sp->playfield[x][y];
3677       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
3678
3679       if (element_new == EL_UNKNOWN)
3680         Error(ERR_WARN, "invalid element %d at position %d, %d",
3681               element_old, x, y);
3682
3683       level->field[x][y] = element_new;
3684     }
3685   }
3686
3687   for (i = 0; i < MAX_PLAYERS; i++)
3688     level->initial_player_gravity[i] =
3689       (header->InitialGravity == 1 ? TRUE : FALSE);
3690
3691   /* skip leading spaces */
3692   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
3693     if (header->LevelTitle[i] != ' ')
3694       break;
3695
3696   /* copy level title */
3697   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
3698     level->name[j] = header->LevelTitle[i];
3699   level->name[j] = '\0';
3700
3701   /* cut trailing spaces */
3702   for (; j > 0; j--)
3703     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
3704       level->name[j - 1] = '\0';
3705
3706   level->gems_needed = header->InfotronsNeeded;
3707
3708   for (i = 0; i < header->SpecialPortCount; i++)
3709   {
3710     SpecialPortType *port = &header->SpecialPort[i];
3711     int port_location = port->PortLocation;
3712     int gravity = port->Gravity;
3713     int port_x, port_y, port_element;
3714
3715     port_x = (port_location / 2) % level->fieldx;
3716     port_y = (port_location / 2) / level->fieldx;
3717
3718     if (port_x < 0 || port_x >= level->fieldx ||
3719         port_y < 0 || port_y >= level->fieldy)
3720     {
3721       Error(ERR_WARN, "special port position (%d, %d) out of bounds",
3722             port_x, port_y);
3723
3724       continue;
3725     }
3726
3727     port_element = level->field[port_x][port_y];
3728
3729     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
3730         port_element > EL_SP_GRAVITY_PORT_UP)
3731     {
3732       Error(ERR_WARN, "no special port at position (%d, %d)", port_x, port_y);
3733
3734       continue;
3735     }
3736
3737     /* change previous (wrong) gravity inverting special port to either
3738        gravity enabling special port or gravity disabling special port */
3739     level->field[port_x][port_y] +=
3740       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
3741        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
3742   }
3743
3744   /* change special gravity ports without database entries to normal ports */
3745   for (x = 0; x < level->fieldx; x++)
3746     for (y = 0; y < level->fieldy; y++)
3747       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
3748           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
3749         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
3750
3751   level->time = 0;                      /* no time limit */
3752   level->amoeba_speed = 0;
3753   level->time_magic_wall = 0;
3754   level->time_wheel = 0;
3755   level->amoeba_content = EL_EMPTY;
3756
3757 #if 1
3758   /* original Supaplex does not use score values -- use default values */
3759 #else
3760   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3761     level->score[i] = 0;
3762 #endif
3763
3764   /* there are no yamyams in supaplex levels */
3765   for (i = 0; i < level->num_yamyam_contents; i++)
3766     for (x = 0; x < 3; x++)
3767       for (y = 0; y < 3; y++)
3768         level->yamyam_content[i].e[x][y] = EL_EMPTY;
3769 }
3770
3771 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
3772 {
3773   struct LevelInfo_SP *level_sp = level->native_sp_level;
3774   struct DemoInfo_SP *demo = &level_sp->demo;
3775   int i, j;
3776
3777   /* always start with reliable default values */
3778   demo->is_available = FALSE;
3779   demo->length = 0;
3780
3781   if (TAPE_IS_EMPTY(tape))
3782     return;
3783
3784   demo->level_nr = tape.level_nr;       /* (currently not used) */
3785
3786   level_sp->header.DemoRandomSeed = tape.random_seed;
3787
3788   demo->length = 0;
3789
3790   for (i = 0; i < tape.length; i++)
3791   {
3792     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
3793     int demo_repeat = tape.pos[i].delay;
3794     int demo_entries = (demo_repeat + 15) / 16;
3795
3796     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
3797     {
3798       Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
3799             SP_MAX_TAPE_LEN);
3800
3801       break;
3802     }
3803
3804     for (j = 0; j < demo_repeat / 16; j++)
3805       demo->data[demo->length++] = 0xf0 | demo_action;
3806
3807     if (demo_repeat % 16)
3808       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
3809   }
3810
3811   demo->is_available = TRUE;
3812 }
3813
3814 static void setTapeInfoToDefaults();
3815
3816 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
3817 {
3818   struct LevelInfo_SP *level_sp = level->native_sp_level;
3819   struct DemoInfo_SP *demo = &level_sp->demo;
3820   char *filename = level->file_info.filename;
3821   int i;
3822
3823   /* always start with reliable default values */
3824   setTapeInfoToDefaults();
3825
3826   if (!demo->is_available)
3827     return;
3828
3829   tape.level_nr = demo->level_nr;       /* (currently not used) */
3830   tape.random_seed = level_sp->header.DemoRandomSeed;
3831
3832   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
3833
3834   tape.counter = 0;
3835   tape.pos[tape.counter].delay = 0;
3836
3837   for (i = 0; i < demo->length; i++)
3838   {
3839     int demo_action = demo->data[i] & 0x0f;
3840     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
3841     int tape_action = map_key_SP_to_RND(demo_action);
3842     int tape_repeat = demo_repeat + 1;
3843     byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
3844     boolean success = 0;
3845     int j;
3846
3847     for (j = 0; j < tape_repeat; j++)
3848       success = TapeAddAction(action);
3849
3850     if (!success)
3851     {
3852       Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
3853             MAX_TAPE_LEN);
3854
3855       break;
3856     }
3857   }
3858
3859   TapeHaltRecording();
3860 }
3861
3862
3863 /* ------------------------------------------------------------------------- */
3864 /* functions for loading DC level                                            */
3865 /* ------------------------------------------------------------------------- */
3866
3867 #define DC_LEVEL_HEADER_SIZE            344
3868
3869 unsigned short getDecodedWord_DC(unsigned short data_encoded, boolean init)
3870 {
3871   static int last_data_encoded;
3872   static int offset1;
3873   static int offset2;
3874   int diff;
3875   int diff_hi, diff_lo;
3876   int data_hi, data_lo;
3877   unsigned short data_decoded;
3878
3879   if (init)
3880   {
3881     last_data_encoded = 0;
3882     offset1 = -1;
3883     offset2 = 0;
3884
3885     return 0;
3886   }
3887
3888   diff = data_encoded - last_data_encoded;
3889   diff_hi = diff & ~0xff;
3890   diff_lo = diff &  0xff;
3891
3892   offset2 += diff_lo;
3893
3894   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
3895   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
3896   data_hi = data_hi & 0xff00;
3897
3898   data_decoded = data_hi | data_lo;
3899
3900   last_data_encoded = data_encoded;
3901
3902   offset1 = (offset1 + 1) % 31;
3903   offset2 = offset2 & 0xff;
3904
3905   return data_decoded;
3906 }
3907
3908 int getMappedElement_DC(int element)
3909 {
3910   switch (element)
3911   {
3912     case 0x0000:
3913       element = EL_ROCK;
3914       break;
3915
3916       /* 0x0117 - 0x036e: (?) */
3917       /* EL_DIAMOND */
3918
3919       /* 0x042d - 0x0684: (?) */
3920       /* EL_EMERALD */
3921
3922     case 0x06f1:
3923       element = EL_NUT;
3924       break;
3925
3926     case 0x074c:
3927       element = EL_BOMB;
3928       break;
3929
3930     case 0x07a4:
3931       element = EL_PEARL;
3932       break;
3933
3934     case 0x0823:
3935       element = EL_CRYSTAL;
3936       break;
3937
3938     case 0x0e77:        /* quicksand (boulder) */
3939       element = EL_QUICKSAND_FAST_FULL;
3940       break;
3941
3942     case 0x0e99:        /* slow quicksand (boulder) */
3943       element = EL_QUICKSAND_FULL;
3944       break;
3945
3946     case 0x0ed2:
3947       element = EL_EM_EXIT_OPEN;
3948       break;
3949
3950     case 0x0ee3:
3951       element = EL_EM_EXIT_CLOSED;
3952       break;
3953
3954     case 0x0eeb:
3955       element = EL_EM_STEEL_EXIT_OPEN;
3956       break;
3957
3958     case 0x0efc:
3959       element = EL_EM_STEEL_EXIT_CLOSED;
3960       break;
3961
3962     case 0x0f4f:        /* dynamite (lit 1) */
3963       element = EL_EM_DYNAMITE_ACTIVE;
3964       break;
3965
3966     case 0x0f57:        /* dynamite (lit 2) */
3967       element = EL_EM_DYNAMITE_ACTIVE;
3968       break;
3969
3970     case 0x0f5f:        /* dynamite (lit 3) */
3971       element = EL_EM_DYNAMITE_ACTIVE;
3972       break;
3973
3974     case 0x0f67:        /* dynamite (lit 4) */
3975       element = EL_EM_DYNAMITE_ACTIVE;
3976       break;
3977
3978     case 0x0f81:
3979     case 0x0f82:
3980     case 0x0f83:
3981     case 0x0f84:
3982       element = EL_AMOEBA_WET;
3983       break;
3984
3985     case 0x0f85:
3986       element = EL_AMOEBA_DROP;
3987       break;
3988
3989     case 0x0fb9:
3990       element = EL_DC_MAGIC_WALL;
3991       break;
3992
3993     case 0x0fd0:
3994       element = EL_SPACESHIP_UP;
3995       break;
3996
3997     case 0x0fd9:
3998       element = EL_SPACESHIP_DOWN;
3999       break;
4000
4001     case 0x0ff1:
4002       element = EL_SPACESHIP_LEFT;
4003       break;
4004
4005     case 0x0ff9:
4006       element = EL_SPACESHIP_RIGHT;
4007       break;
4008
4009     case 0x1057:
4010       element = EL_BUG_UP;
4011       break;
4012
4013     case 0x1060:
4014       element = EL_BUG_DOWN;
4015       break;
4016
4017     case 0x1078:
4018       element = EL_BUG_LEFT;
4019       break;
4020
4021     case 0x1080:
4022       element = EL_BUG_RIGHT;
4023       break;
4024
4025     case 0x10de:
4026       element = EL_MOLE_UP;
4027       break;
4028
4029     case 0x10e7:
4030       element = EL_MOLE_DOWN;
4031       break;
4032
4033     case 0x10ff:
4034       element = EL_MOLE_LEFT;
4035       break;
4036
4037     case 0x1107:
4038       element = EL_MOLE_RIGHT;
4039       break;
4040
4041     case 0x11c0:
4042       element = EL_ROBOT;
4043       break;
4044
4045     case 0x13f5:
4046       element = EL_YAMYAM;
4047       break;
4048
4049     case 0x1425:
4050       element = EL_SWITCHGATE_OPEN;
4051       break;
4052
4053     case 0x1426:
4054       element = EL_SWITCHGATE_CLOSED;
4055       break;
4056
4057     case 0x1437:
4058       element = EL_DC_SWITCHGATE_SWITCH_UP;
4059       break;
4060
4061     case 0x143a:
4062       element = EL_TIMEGATE_CLOSED;
4063       break;
4064
4065     case 0x144c:        /* conveyor belt switch (green) */
4066       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
4067       break;
4068
4069     case 0x144f:        /* conveyor belt switch (red) */
4070       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
4071       break;
4072
4073     case 0x1452:        /* conveyor belt switch (blue) */
4074       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
4075       break;
4076
4077     case 0x145b:
4078       element = EL_CONVEYOR_BELT_3_MIDDLE;
4079       break;
4080
4081     case 0x1463:
4082       element = EL_CONVEYOR_BELT_3_LEFT;
4083       break;
4084
4085     case 0x146b:
4086       element = EL_CONVEYOR_BELT_3_RIGHT;
4087       break;
4088
4089     case 0x1473:
4090       element = EL_CONVEYOR_BELT_1_MIDDLE;
4091       break;
4092
4093     case 0x147b:
4094       element = EL_CONVEYOR_BELT_1_LEFT;
4095       break;
4096
4097     case 0x1483:
4098       element = EL_CONVEYOR_BELT_1_RIGHT;
4099       break;
4100
4101     case 0x148b:
4102       element = EL_CONVEYOR_BELT_4_MIDDLE;
4103       break;
4104
4105     case 0x1493:
4106       element = EL_CONVEYOR_BELT_4_LEFT;
4107       break;
4108
4109     case 0x149b:
4110       element = EL_CONVEYOR_BELT_4_RIGHT;
4111       break;
4112
4113     case 0x14ac:
4114       element = EL_EXPANDABLE_WALL_HORIZONTAL;
4115       break;
4116
4117     case 0x14bd:
4118       element = EL_EXPANDABLE_WALL_VERTICAL;
4119       break;
4120
4121     case 0x14c6:
4122       element = EL_EXPANDABLE_WALL_ANY;
4123       break;
4124
4125     case 0x14ce:        /* growing steel wall (left/right) */
4126       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
4127       break;
4128
4129     case 0x14df:        /* growing steel wall (up/down) */
4130       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
4131       break;
4132
4133     case 0x14e8:        /* growing steel wall (up/down/left/right) */
4134       element = EL_EXPANDABLE_STEELWALL_ANY;
4135       break;
4136
4137     case 0x14e9:
4138       element = EL_SHIELD_DEADLY;
4139       break;
4140
4141     case 0x1501:
4142       element = EL_EXTRA_TIME;
4143       break;
4144
4145     case 0x154f:
4146       element = EL_ACID;
4147       break;
4148
4149     case 0x1577:
4150       element = EL_EMPTY_SPACE;
4151       break;
4152
4153     case 0x1578:        /* quicksand (empty) */
4154       element = EL_QUICKSAND_FAST_EMPTY;
4155       break;
4156
4157     case 0x1579:        /* slow quicksand (empty) */
4158       element = EL_QUICKSAND_EMPTY;
4159       break;
4160
4161       /* 0x157c - 0x158b: */
4162       /* EL_SAND */
4163
4164       /* 0x1590 - 0x159f: */
4165       /* EL_DC_LANDMINE */
4166
4167     case 0x15a0:
4168       element = EL_EM_DYNAMITE;
4169       break;
4170
4171     case 0x15a1:        /* key (red) */
4172       element = EL_EM_KEY_1;
4173       break;
4174
4175     case 0x15a2:        /* key (yellow) */
4176       element = EL_EM_KEY_2;
4177       break;
4178
4179     case 0x15a3:        /* key (blue) */
4180       element = EL_EM_KEY_4;
4181       break;
4182
4183     case 0x15a4:        /* key (green) */
4184       element = EL_EM_KEY_3;
4185       break;
4186
4187     case 0x15a5:        /* key (white) */
4188       element = EL_DC_KEY_WHITE;
4189       break;
4190
4191     case 0x15a6:
4192       element = EL_WALL_SLIPPERY;
4193       break;
4194
4195     case 0x15a7:
4196       element = EL_WALL;
4197       break;
4198
4199     case 0x15a8:        /* wall (not round) */
4200       element = EL_WALL;
4201       break;
4202
4203     case 0x15a9:        /* (blue) */
4204       element = EL_CHAR_A;
4205       break;
4206
4207     case 0x15aa:        /* (blue) */
4208       element = EL_CHAR_B;
4209       break;
4210
4211     case 0x15ab:        /* (blue) */
4212       element = EL_CHAR_C;
4213       break;
4214
4215     case 0x15ac:        /* (blue) */
4216       element = EL_CHAR_D;
4217       break;
4218
4219     case 0x15ad:        /* (blue) */
4220       element = EL_CHAR_E;
4221       break;
4222
4223     case 0x15ae:        /* (blue) */
4224       element = EL_CHAR_F;
4225       break;
4226
4227     case 0x15af:        /* (blue) */
4228       element = EL_CHAR_G;
4229       break;
4230
4231     case 0x15b0:        /* (blue) */
4232       element = EL_CHAR_H;
4233       break;
4234
4235     case 0x15b1:        /* (blue) */
4236       element = EL_CHAR_I;
4237       break;
4238
4239     case 0x15b2:        /* (blue) */
4240       element = EL_CHAR_J;
4241       break;
4242
4243     case 0x15b3:        /* (blue) */
4244       element = EL_CHAR_K;
4245       break;
4246
4247     case 0x15b4:        /* (blue) */
4248       element = EL_CHAR_L;
4249       break;
4250
4251     case 0x15b5:        /* (blue) */
4252       element = EL_CHAR_M;
4253       break;
4254
4255     case 0x15b6:        /* (blue) */
4256       element = EL_CHAR_N;
4257       break;
4258
4259     case 0x15b7:        /* (blue) */
4260       element = EL_CHAR_O;
4261       break;
4262
4263     case 0x15b8:        /* (blue) */
4264       element = EL_CHAR_P;
4265       break;
4266
4267     case 0x15b9:        /* (blue) */
4268       element = EL_CHAR_Q;
4269       break;
4270
4271     case 0x15ba:        /* (blue) */
4272       element = EL_CHAR_R;
4273       break;
4274
4275     case 0x15bb:        /* (blue) */
4276       element = EL_CHAR_S;
4277       break;
4278
4279     case 0x15bc:        /* (blue) */
4280       element = EL_CHAR_T;
4281       break;
4282
4283     case 0x15bd:        /* (blue) */
4284       element = EL_CHAR_U;
4285       break;
4286
4287     case 0x15be:        /* (blue) */
4288       element = EL_CHAR_V;
4289       break;
4290
4291     case 0x15bf:        /* (blue) */
4292       element = EL_CHAR_W;
4293       break;
4294
4295     case 0x15c0:        /* (blue) */
4296       element = EL_CHAR_X;
4297       break;
4298
4299     case 0x15c1:        /* (blue) */
4300       element = EL_CHAR_Y;
4301       break;
4302
4303     case 0x15c2:        /* (blue) */
4304       element = EL_CHAR_Z;
4305       break;
4306
4307     case 0x15c3:        /* (blue) */
4308       element = EL_CHAR_AUMLAUT;
4309       break;
4310
4311     case 0x15c4:        /* (blue) */
4312       element = EL_CHAR_OUMLAUT;
4313       break;
4314
4315     case 0x15c5:        /* (blue) */
4316       element = EL_CHAR_UUMLAUT;
4317       break;
4318
4319     case 0x15c6:        /* (blue) */
4320       element = EL_CHAR_0;
4321       break;
4322
4323     case 0x15c7:        /* (blue) */
4324       element = EL_CHAR_1;
4325       break;
4326
4327     case 0x15c8:        /* (blue) */
4328       element = EL_CHAR_2;
4329       break;
4330
4331     case 0x15c9:        /* (blue) */
4332       element = EL_CHAR_3;
4333       break;
4334
4335     case 0x15ca:        /* (blue) */
4336       element = EL_CHAR_4;
4337       break;
4338
4339     case 0x15cb:        /* (blue) */
4340       element = EL_CHAR_5;
4341       break;
4342
4343     case 0x15cc:        /* (blue) */
4344       element = EL_CHAR_6;
4345       break;
4346
4347     case 0x15cd:        /* (blue) */
4348       element = EL_CHAR_7;
4349       break;
4350
4351     case 0x15ce:        /* (blue) */
4352       element = EL_CHAR_8;
4353       break;
4354
4355     case 0x15cf:        /* (blue) */
4356       element = EL_CHAR_9;
4357       break;
4358
4359     case 0x15d0:        /* (blue) */
4360       element = EL_CHAR_PERIOD;
4361       break;
4362
4363     case 0x15d1:        /* (blue) */
4364       element = EL_CHAR_EXCLAM;
4365       break;
4366
4367     case 0x15d2:        /* (blue) */
4368       element = EL_CHAR_COLON;
4369       break;
4370
4371     case 0x15d3:        /* (blue) */
4372       element = EL_CHAR_LESS;
4373       break;
4374
4375     case 0x15d4:        /* (blue) */
4376       element = EL_CHAR_GREATER;
4377       break;
4378
4379     case 0x15d5:        /* (blue) */
4380       element = EL_CHAR_QUESTION;
4381       break;
4382
4383     case 0x15d6:        /* (blue) */
4384       element = EL_CHAR_COPYRIGHT;
4385       break;
4386
4387     case 0x15d7:        /* (blue) */
4388       element = EL_CHAR_UP;
4389       break;
4390
4391     case 0x15d8:        /* (blue) */
4392       element = EL_CHAR_DOWN;
4393       break;
4394
4395     case 0x15d9:        /* (blue) */
4396       element = EL_CHAR_BUTTON;
4397       break;
4398
4399     case 0x15da:        /* (blue) */
4400       element = EL_CHAR_PLUS;
4401       break;
4402
4403     case 0x15db:        /* (blue) */
4404       element = EL_CHAR_MINUS;
4405       break;
4406
4407     case 0x15dc:        /* (blue) */
4408       element = EL_CHAR_APOSTROPHE;
4409       break;
4410
4411     case 0x15dd:        /* (blue) */
4412       element = EL_CHAR_PARENLEFT;
4413       break;
4414
4415     case 0x15de:        /* (blue) */
4416       element = EL_CHAR_PARENRIGHT;
4417       break;
4418
4419     case 0x15df:        /* (green) */
4420       element = EL_CHAR_A;
4421       break;
4422
4423     case 0x15e0:        /* (green) */
4424       element = EL_CHAR_B;
4425       break;
4426
4427     case 0x15e1:        /* (green) */
4428       element = EL_CHAR_C;
4429       break;
4430
4431     case 0x15e2:        /* (green) */
4432       element = EL_CHAR_D;
4433       break;
4434
4435     case 0x15e3:        /* (green) */
4436       element = EL_CHAR_E;
4437       break;
4438
4439     case 0x15e4:        /* (green) */
4440       element = EL_CHAR_F;
4441       break;
4442
4443     case 0x15e5:        /* (green) */
4444       element = EL_CHAR_G;
4445       break;
4446
4447     case 0x15e6:        /* (green) */
4448       element = EL_CHAR_H;
4449       break;
4450
4451     case 0x15e7:        /* (green) */
4452       element = EL_CHAR_I;
4453       break;
4454
4455     case 0x15e8:        /* (green) */
4456       element = EL_CHAR_J;
4457       break;
4458
4459     case 0x15e9:        /* (green) */
4460       element = EL_CHAR_K;
4461       break;
4462
4463     case 0x15ea:        /* (green) */
4464       element = EL_CHAR_L;
4465       break;
4466
4467     case 0x15eb:        /* (green) */
4468       element = EL_CHAR_M;
4469       break;
4470
4471     case 0x15ec:        /* (green) */
4472       element = EL_CHAR_N;
4473       break;
4474
4475     case 0x15ed:        /* (green) */
4476       element = EL_CHAR_O;
4477       break;
4478
4479     case 0x15ee:        /* (green) */
4480       element = EL_CHAR_P;
4481       break;
4482
4483     case 0x15ef:        /* (green) */
4484       element = EL_CHAR_Q;
4485       break;
4486
4487     case 0x15f0:        /* (green) */
4488       element = EL_CHAR_R;
4489       break;
4490
4491     case 0x15f1:        /* (green) */
4492       element = EL_CHAR_S;
4493       break;
4494
4495     case 0x15f2:        /* (green) */
4496       element = EL_CHAR_T;
4497       break;
4498
4499     case 0x15f3:        /* (green) */
4500       element = EL_CHAR_U;
4501       break;
4502
4503     case 0x15f4:        /* (green) */
4504       element = EL_CHAR_V;
4505       break;
4506
4507     case 0x15f5:        /* (green) */
4508       element = EL_CHAR_W;
4509       break;
4510
4511     case 0x15f6:        /* (green) */
4512       element = EL_CHAR_X;
4513       break;
4514
4515     case 0x15f7:        /* (green) */
4516       element = EL_CHAR_Y;
4517       break;
4518
4519     case 0x15f8:        /* (green) */
4520       element = EL_CHAR_Z;
4521       break;
4522
4523     case 0x15f9:        /* (green) */
4524       element = EL_CHAR_AUMLAUT;
4525       break;
4526
4527     case 0x15fa:        /* (green) */
4528       element = EL_CHAR_OUMLAUT;
4529       break;
4530
4531     case 0x15fb:        /* (green) */
4532       element = EL_CHAR_UUMLAUT;
4533       break;
4534
4535     case 0x15fc:        /* (green) */
4536       element = EL_CHAR_0;
4537       break;
4538
4539     case 0x15fd:        /* (green) */
4540       element = EL_CHAR_1;
4541       break;
4542
4543     case 0x15fe:        /* (green) */
4544       element = EL_CHAR_2;
4545       break;
4546
4547     case 0x15ff:        /* (green) */
4548       element = EL_CHAR_3;
4549       break;
4550
4551     case 0x1600:        /* (green) */
4552       element = EL_CHAR_4;
4553       break;
4554
4555     case 0x1601:        /* (green) */
4556       element = EL_CHAR_5;
4557       break;
4558
4559     case 0x1602:        /* (green) */
4560       element = EL_CHAR_6;
4561       break;
4562
4563     case 0x1603:        /* (green) */
4564       element = EL_CHAR_7;
4565       break;
4566
4567     case 0x1604:        /* (green) */
4568       element = EL_CHAR_8;
4569       break;
4570
4571     case 0x1605:        /* (green) */
4572       element = EL_CHAR_9;
4573       break;
4574
4575     case 0x1606:        /* (green) */
4576       element = EL_CHAR_PERIOD;
4577       break;
4578
4579     case 0x1607:        /* (green) */
4580       element = EL_CHAR_EXCLAM;
4581       break;
4582
4583     case 0x1608:        /* (green) */
4584       element = EL_CHAR_COLON;
4585       break;
4586
4587     case 0x1609:        /* (green) */
4588       element = EL_CHAR_LESS;
4589       break;
4590
4591     case 0x160a:        /* (green) */
4592       element = EL_CHAR_GREATER;
4593       break;
4594
4595     case 0x160b:        /* (green) */
4596       element = EL_CHAR_QUESTION;
4597       break;
4598
4599     case 0x160c:        /* (green) */
4600       element = EL_CHAR_COPYRIGHT;
4601       break;
4602
4603     case 0x160d:        /* (green) */
4604       element = EL_CHAR_UP;
4605       break;
4606
4607     case 0x160e:        /* (green) */
4608       element = EL_CHAR_DOWN;
4609       break;
4610
4611     case 0x160f:        /* (green) */
4612       element = EL_CHAR_BUTTON;
4613       break;
4614
4615     case 0x1610:        /* (green) */
4616       element = EL_CHAR_PLUS;
4617       break;
4618
4619     case 0x1611:        /* (green) */
4620       element = EL_CHAR_MINUS;
4621       break;
4622
4623     case 0x1612:        /* (green) */
4624       element = EL_CHAR_APOSTROPHE;
4625       break;
4626
4627     case 0x1613:        /* (green) */
4628       element = EL_CHAR_PARENLEFT;
4629       break;
4630
4631     case 0x1614:        /* (green) */
4632       element = EL_CHAR_PARENRIGHT;
4633       break;
4634
4635     case 0x1615:        /* (blue steel) */
4636       element = EL_STEEL_CHAR_A;
4637       break;
4638
4639     case 0x1616:        /* (blue steel) */
4640       element = EL_STEEL_CHAR_B;
4641       break;
4642
4643     case 0x1617:        /* (blue steel) */
4644       element = EL_STEEL_CHAR_C;
4645       break;
4646
4647     case 0x1618:        /* (blue steel) */
4648       element = EL_STEEL_CHAR_D;
4649       break;
4650
4651     case 0x1619:        /* (blue steel) */
4652       element = EL_STEEL_CHAR_E;
4653       break;
4654
4655     case 0x161a:        /* (blue steel) */
4656       element = EL_STEEL_CHAR_F;
4657       break;
4658
4659     case 0x161b:        /* (blue steel) */
4660       element = EL_STEEL_CHAR_G;
4661       break;
4662
4663     case 0x161c:        /* (blue steel) */
4664       element = EL_STEEL_CHAR_H;
4665       break;
4666
4667     case 0x161d:        /* (blue steel) */
4668       element = EL_STEEL_CHAR_I;
4669       break;
4670
4671     case 0x161e:        /* (blue steel) */
4672       element = EL_STEEL_CHAR_J;
4673       break;
4674
4675     case 0x161f:        /* (blue steel) */
4676       element = EL_STEEL_CHAR_K;
4677       break;
4678
4679     case 0x1620:        /* (blue steel) */
4680       element = EL_STEEL_CHAR_L;
4681       break;
4682
4683     case 0x1621:        /* (blue steel) */
4684       element = EL_STEEL_CHAR_M;
4685       break;
4686
4687     case 0x1622:        /* (blue steel) */
4688       element = EL_STEEL_CHAR_N;
4689       break;
4690
4691     case 0x1623:        /* (blue steel) */
4692       element = EL_STEEL_CHAR_O;
4693       break;
4694
4695     case 0x1624:        /* (blue steel) */
4696       element = EL_STEEL_CHAR_P;
4697       break;
4698
4699     case 0x1625:        /* (blue steel) */
4700       element = EL_STEEL_CHAR_Q;
4701       break;
4702
4703     case 0x1626:        /* (blue steel) */
4704       element = EL_STEEL_CHAR_R;
4705       break;
4706
4707     case 0x1627:        /* (blue steel) */
4708       element = EL_STEEL_CHAR_S;
4709       break;
4710
4711     case 0x1628:        /* (blue steel) */
4712       element = EL_STEEL_CHAR_T;
4713       break;
4714
4715     case 0x1629:        /* (blue steel) */
4716       element = EL_STEEL_CHAR_U;
4717       break;
4718
4719     case 0x162a:        /* (blue steel) */
4720       element = EL_STEEL_CHAR_V;
4721       break;
4722
4723     case 0x162b:        /* (blue steel) */
4724       element = EL_STEEL_CHAR_W;
4725       break;
4726
4727     case 0x162c:        /* (blue steel) */
4728       element = EL_STEEL_CHAR_X;
4729       break;
4730
4731     case 0x162d:        /* (blue steel) */
4732       element = EL_STEEL_CHAR_Y;
4733       break;
4734
4735     case 0x162e:        /* (blue steel) */
4736       element = EL_STEEL_CHAR_Z;
4737       break;
4738
4739     case 0x162f:        /* (blue steel) */
4740       element = EL_STEEL_CHAR_AUMLAUT;
4741       break;
4742
4743     case 0x1630:        /* (blue steel) */
4744       element = EL_STEEL_CHAR_OUMLAUT;
4745       break;
4746
4747     case 0x1631:        /* (blue steel) */
4748       element = EL_STEEL_CHAR_UUMLAUT;
4749       break;
4750
4751     case 0x1632:        /* (blue steel) */
4752       element = EL_STEEL_CHAR_0;
4753       break;
4754
4755     case 0x1633:        /* (blue steel) */
4756       element = EL_STEEL_CHAR_1;
4757       break;
4758
4759     case 0x1634:        /* (blue steel) */
4760       element = EL_STEEL_CHAR_2;
4761       break;
4762
4763     case 0x1635:        /* (blue steel) */
4764       element = EL_STEEL_CHAR_3;
4765       break;
4766
4767     case 0x1636:        /* (blue steel) */
4768       element = EL_STEEL_CHAR_4;
4769       break;
4770
4771     case 0x1637:        /* (blue steel) */
4772       element = EL_STEEL_CHAR_5;
4773       break;
4774
4775     case 0x1638:        /* (blue steel) */
4776       element = EL_STEEL_CHAR_6;
4777       break;
4778
4779     case 0x1639:        /* (blue steel) */
4780       element = EL_STEEL_CHAR_7;
4781       break;
4782
4783     case 0x163a:        /* (blue steel) */
4784       element = EL_STEEL_CHAR_8;
4785       break;
4786
4787     case 0x163b:        /* (blue steel) */
4788       element = EL_STEEL_CHAR_9;
4789       break;
4790
4791     case 0x163c:        /* (blue steel) */
4792       element = EL_STEEL_CHAR_PERIOD;
4793       break;
4794
4795     case 0x163d:        /* (blue steel) */
4796       element = EL_STEEL_CHAR_EXCLAM;
4797       break;
4798
4799     case 0x163e:        /* (blue steel) */
4800       element = EL_STEEL_CHAR_COLON;
4801       break;
4802
4803     case 0x163f:        /* (blue steel) */
4804       element = EL_STEEL_CHAR_LESS;
4805       break;
4806
4807     case 0x1640:        /* (blue steel) */
4808       element = EL_STEEL_CHAR_GREATER;
4809       break;
4810
4811     case 0x1641:        /* (blue steel) */
4812       element = EL_STEEL_CHAR_QUESTION;
4813       break;
4814
4815     case 0x1642:        /* (blue steel) */
4816       element = EL_STEEL_CHAR_COPYRIGHT;
4817       break;
4818
4819     case 0x1643:        /* (blue steel) */
4820       element = EL_STEEL_CHAR_UP;
4821       break;
4822
4823     case 0x1644:        /* (blue steel) */
4824       element = EL_STEEL_CHAR_DOWN;
4825       break;
4826
4827     case 0x1645:        /* (blue steel) */
4828       element = EL_STEEL_CHAR_BUTTON;
4829       break;
4830
4831     case 0x1646:        /* (blue steel) */
4832       element = EL_STEEL_CHAR_PLUS;
4833       break;
4834
4835     case 0x1647:        /* (blue steel) */
4836       element = EL_STEEL_CHAR_MINUS;
4837       break;
4838
4839     case 0x1648:        /* (blue steel) */
4840       element = EL_STEEL_CHAR_APOSTROPHE;
4841       break;
4842
4843     case 0x1649:        /* (blue steel) */
4844       element = EL_STEEL_CHAR_PARENLEFT;
4845       break;
4846
4847     case 0x164a:        /* (blue steel) */
4848       element = EL_STEEL_CHAR_PARENRIGHT;
4849       break;
4850
4851     case 0x164b:        /* (green steel) */
4852       element = EL_STEEL_CHAR_A;
4853       break;
4854
4855     case 0x164c:        /* (green steel) */
4856       element = EL_STEEL_CHAR_B;
4857       break;
4858
4859     case 0x164d:        /* (green steel) */
4860       element = EL_STEEL_CHAR_C;
4861       break;
4862
4863     case 0x164e:        /* (green steel) */
4864       element = EL_STEEL_CHAR_D;
4865       break;
4866
4867     case 0x164f:        /* (green steel) */
4868       element = EL_STEEL_CHAR_E;
4869       break;
4870
4871     case 0x1650:        /* (green steel) */
4872       element = EL_STEEL_CHAR_F;
4873       break;
4874
4875     case 0x1651:        /* (green steel) */
4876       element = EL_STEEL_CHAR_G;
4877       break;
4878
4879     case 0x1652:        /* (green steel) */
4880       element = EL_STEEL_CHAR_H;
4881       break;
4882
4883     case 0x1653:        /* (green steel) */
4884       element = EL_STEEL_CHAR_I;
4885       break;
4886
4887     case 0x1654:        /* (green steel) */
4888       element = EL_STEEL_CHAR_J;
4889       break;
4890
4891     case 0x1655:        /* (green steel) */
4892       element = EL_STEEL_CHAR_K;
4893       break;
4894
4895     case 0x1656:        /* (green steel) */
4896       element = EL_STEEL_CHAR_L;
4897       break;
4898
4899     case 0x1657:        /* (green steel) */
4900       element = EL_STEEL_CHAR_M;
4901       break;
4902
4903     case 0x1658:        /* (green steel) */
4904       element = EL_STEEL_CHAR_N;
4905       break;
4906
4907     case 0x1659:        /* (green steel) */
4908       element = EL_STEEL_CHAR_O;
4909       break;
4910
4911     case 0x165a:        /* (green steel) */
4912       element = EL_STEEL_CHAR_P;
4913       break;
4914
4915     case 0x165b:        /* (green steel) */
4916       element = EL_STEEL_CHAR_Q;
4917       break;
4918
4919     case 0x165c:        /* (green steel) */
4920       element = EL_STEEL_CHAR_R;
4921       break;
4922
4923     case 0x165d:        /* (green steel) */
4924       element = EL_STEEL_CHAR_S;
4925       break;
4926
4927     case 0x165e:        /* (green steel) */
4928       element = EL_STEEL_CHAR_T;
4929       break;
4930
4931     case 0x165f:        /* (green steel) */
4932       element = EL_STEEL_CHAR_U;
4933       break;
4934
4935     case 0x1660:        /* (green steel) */
4936       element = EL_STEEL_CHAR_V;
4937       break;
4938
4939     case 0x1661:        /* (green steel) */
4940       element = EL_STEEL_CHAR_W;
4941       break;
4942
4943     case 0x1662:        /* (green steel) */
4944       element = EL_STEEL_CHAR_X;
4945       break;
4946
4947     case 0x1663:        /* (green steel) */
4948       element = EL_STEEL_CHAR_Y;
4949       break;
4950
4951     case 0x1664:        /* (green steel) */
4952       element = EL_STEEL_CHAR_Z;
4953       break;
4954
4955     case 0x1665:        /* (green steel) */
4956       element = EL_STEEL_CHAR_AUMLAUT;
4957       break;
4958
4959     case 0x1666:        /* (green steel) */
4960       element = EL_STEEL_CHAR_OUMLAUT;
4961       break;
4962
4963     case 0x1667:        /* (green steel) */
4964       element = EL_STEEL_CHAR_UUMLAUT;
4965       break;
4966
4967     case 0x1668:        /* (green steel) */
4968       element = EL_STEEL_CHAR_0;
4969       break;
4970
4971     case 0x1669:        /* (green steel) */
4972       element = EL_STEEL_CHAR_1;
4973       break;
4974
4975     case 0x166a:        /* (green steel) */
4976       element = EL_STEEL_CHAR_2;
4977       break;
4978
4979     case 0x166b:        /* (green steel) */
4980       element = EL_STEEL_CHAR_3;
4981       break;
4982
4983     case 0x166c:        /* (green steel) */
4984       element = EL_STEEL_CHAR_4;
4985       break;
4986
4987     case 0x166d:        /* (green steel) */
4988       element = EL_STEEL_CHAR_5;
4989       break;
4990
4991     case 0x166e:        /* (green steel) */
4992       element = EL_STEEL_CHAR_6;
4993       break;
4994
4995     case 0x166f:        /* (green steel) */
4996       element = EL_STEEL_CHAR_7;
4997       break;
4998
4999     case 0x1670:        /* (green steel) */
5000       element = EL_STEEL_CHAR_8;
5001       break;
5002
5003     case 0x1671:        /* (green steel) */
5004       element = EL_STEEL_CHAR_9;
5005       break;
5006
5007     case 0x1672:        /* (green steel) */
5008       element = EL_STEEL_CHAR_PERIOD;
5009       break;
5010
5011     case 0x1673:        /* (green steel) */
5012       element = EL_STEEL_CHAR_EXCLAM;
5013       break;
5014
5015     case 0x1674:        /* (green steel) */
5016       element = EL_STEEL_CHAR_COLON;
5017       break;
5018
5019     case 0x1675:        /* (green steel) */
5020       element = EL_STEEL_CHAR_LESS;
5021       break;
5022
5023     case 0x1676:        /* (green steel) */
5024       element = EL_STEEL_CHAR_GREATER;
5025       break;
5026
5027     case 0x1677:        /* (green steel) */
5028       element = EL_STEEL_CHAR_QUESTION;
5029       break;
5030
5031     case 0x1678:        /* (green steel) */
5032       element = EL_STEEL_CHAR_COPYRIGHT;
5033       break;
5034
5035     case 0x1679:        /* (green steel) */
5036       element = EL_STEEL_CHAR_UP;
5037       break;
5038
5039     case 0x167a:        /* (green steel) */
5040       element = EL_STEEL_CHAR_DOWN;
5041       break;
5042
5043     case 0x167b:        /* (green steel) */
5044       element = EL_STEEL_CHAR_BUTTON;
5045       break;
5046
5047     case 0x167c:        /* (green steel) */
5048       element = EL_STEEL_CHAR_PLUS;
5049       break;
5050
5051     case 0x167d:        /* (green steel) */
5052       element = EL_STEEL_CHAR_MINUS;
5053       break;
5054
5055     case 0x167e:        /* (green steel) */
5056       element = EL_STEEL_CHAR_APOSTROPHE;
5057       break;
5058
5059     case 0x167f:        /* (green steel) */
5060       element = EL_STEEL_CHAR_PARENLEFT;
5061       break;
5062
5063     case 0x1680:        /* (green steel) */
5064       element = EL_STEEL_CHAR_PARENRIGHT;
5065       break;
5066
5067     case 0x1681:        /* gate (red) */
5068       element = EL_EM_GATE_1;
5069       break;
5070
5071     case 0x1682:        /* secret gate (red) */
5072       element = EL_GATE_1_GRAY;
5073       break;
5074
5075     case 0x1683:        /* gate (yellow) */
5076       element = EL_EM_GATE_2;
5077       break;
5078
5079     case 0x1684:        /* secret gate (yellow) */
5080       element = EL_GATE_2_GRAY;
5081       break;
5082
5083     case 0x1685:        /* gate (blue) */
5084       element = EL_EM_GATE_4;
5085       break;
5086
5087     case 0x1686:        /* secret gate (blue) */
5088       element = EL_GATE_4_GRAY;
5089       break;
5090
5091     case 0x1687:        /* gate (green) */
5092       element = EL_EM_GATE_3;
5093       break;
5094
5095     case 0x1688:        /* secret gate (green) */
5096       element = EL_GATE_3_GRAY;
5097       break;
5098
5099     case 0x1689:        /* gate (white) */
5100       element = EL_DC_GATE_WHITE;
5101       break;
5102
5103     case 0x168a:        /* secret gate (white) */
5104       element = EL_DC_GATE_WHITE_GRAY;
5105       break;
5106
5107     case 0x168b:        /* secret gate (no key) */
5108       element = EL_DC_GATE_FAKE_GRAY;
5109       break;
5110
5111     case 0x168c:
5112       element = EL_ROBOT_WHEEL;
5113       break;
5114
5115     case 0x168d:
5116       element = EL_DC_TIMEGATE_SWITCH;
5117       break;
5118
5119     case 0x168e:
5120       element = EL_ACID_POOL_BOTTOM;
5121       break;
5122
5123     case 0x168f:
5124       element = EL_ACID_POOL_TOPLEFT;
5125       break;
5126
5127     case 0x1690:
5128       element = EL_ACID_POOL_TOPRIGHT;
5129       break;
5130
5131     case 0x1691:
5132       element = EL_ACID_POOL_BOTTOMLEFT;
5133       break;
5134
5135     case 0x1692:
5136       element = EL_ACID_POOL_BOTTOMRIGHT;
5137       break;
5138
5139     case 0x1693:
5140       element = EL_STEELWALL;
5141       break;
5142
5143     case 0x1694:
5144       element = EL_STEELWALL_SLIPPERY;
5145       break;
5146
5147     case 0x1695:        /* steel wall (not round) */
5148       element = EL_STEELWALL;
5149       break;
5150
5151     case 0x1696:        /* steel wall (left) */
5152       element = EL_DC_STEELWALL_1_LEFT;
5153       break;
5154
5155     case 0x1697:        /* steel wall (bottom) */
5156       element = EL_DC_STEELWALL_1_BOTTOM;
5157       break;
5158
5159     case 0x1698:        /* steel wall (right) */
5160       element = EL_DC_STEELWALL_1_RIGHT;
5161       break;
5162
5163     case 0x1699:        /* steel wall (top) */
5164       element = EL_DC_STEELWALL_1_TOP;
5165       break;
5166
5167     case 0x169a:        /* steel wall (left/bottom) */
5168       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
5169       break;
5170
5171     case 0x169b:        /* steel wall (right/bottom) */
5172       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
5173       break;
5174
5175     case 0x169c:        /* steel wall (right/top) */
5176       element = EL_DC_STEELWALL_1_TOPRIGHT;
5177       break;
5178
5179     case 0x169d:        /* steel wall (left/top) */
5180       element = EL_DC_STEELWALL_1_TOPLEFT;
5181       break;
5182
5183     case 0x169e:        /* steel wall (right/bottom small) */
5184       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
5185       break;
5186
5187     case 0x169f:        /* steel wall (left/bottom small) */
5188       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
5189       break;
5190
5191     case 0x16a0:        /* steel wall (right/top small) */
5192       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
5193       break;
5194
5195     case 0x16a1:        /* steel wall (left/top small) */
5196       element = EL_DC_STEELWALL_1_TOPLEFT_2;
5197       break;
5198
5199     case 0x16a2:        /* steel wall (left/right) */
5200       element = EL_DC_STEELWALL_1_VERTICAL;
5201       break;
5202
5203     case 0x16a3:        /* steel wall (top/bottom) */
5204       element = EL_DC_STEELWALL_1_HORIZONTAL;
5205       break;
5206
5207     case 0x16a4:        /* steel wall 2 (left end) */
5208       element = EL_DC_STEELWALL_2_LEFT;
5209       break;
5210
5211     case 0x16a5:        /* steel wall 2 (right end) */
5212       element = EL_DC_STEELWALL_2_RIGHT;
5213       break;
5214
5215     case 0x16a6:        /* steel wall 2 (top end) */
5216       element = EL_DC_STEELWALL_2_TOP;
5217       break;
5218
5219     case 0x16a7:        /* steel wall 2 (bottom end) */
5220       element = EL_DC_STEELWALL_2_BOTTOM;
5221       break;
5222
5223     case 0x16a8:        /* steel wall 2 (left/right) */
5224       element = EL_DC_STEELWALL_2_HORIZONTAL;
5225       break;
5226
5227     case 0x16a9:        /* steel wall 2 (up/down) */
5228       element = EL_DC_STEELWALL_2_VERTICAL;
5229       break;
5230
5231     case 0x16aa:        /* steel wall 2 (mid) */
5232       element = EL_DC_STEELWALL_2_MIDDLE;
5233       break;
5234
5235     case 0x16ab:
5236       element = EL_SIGN_EXCLAMATION;
5237       break;
5238
5239     case 0x16ac:
5240       element = EL_SIGN_RADIOACTIVITY;
5241       break;
5242
5243     case 0x16ad:
5244       element = EL_SIGN_STOP;
5245       break;
5246
5247     case 0x16ae:
5248       element = EL_SIGN_WHEELCHAIR;
5249       break;
5250
5251     case 0x16af:
5252       element = EL_SIGN_PARKING;
5253       break;
5254
5255     case 0x16b0:
5256       element = EL_SIGN_NO_ENTRY;
5257       break;
5258
5259     case 0x16b1:
5260       element = EL_SIGN_HEART;
5261       break;
5262
5263     case 0x16b2:
5264       element = EL_SIGN_GIVE_WAY;
5265       break;
5266
5267     case 0x16b3:
5268       element = EL_SIGN_ENTRY_FORBIDDEN;
5269       break;
5270
5271     case 0x16b4:
5272       element = EL_SIGN_EMERGENCY_EXIT;
5273       break;
5274
5275     case 0x16b5:
5276       element = EL_SIGN_YIN_YANG;
5277       break;
5278
5279     case 0x16b6:
5280       element = EL_WALL_EMERALD;
5281       break;
5282
5283     case 0x16b7:
5284       element = EL_WALL_DIAMOND;
5285       break;
5286
5287     case 0x16b8:
5288       element = EL_WALL_PEARL;
5289       break;
5290
5291     case 0x16b9:
5292       element = EL_WALL_CRYSTAL;
5293       break;
5294
5295     case 0x16ba:
5296       element = EL_INVISIBLE_WALL;
5297       break;
5298
5299     case 0x16bb:
5300       element = EL_INVISIBLE_STEELWALL;
5301       break;
5302
5303       /* 0x16bc - 0x16cb: */
5304       /* EL_INVISIBLE_SAND */
5305
5306     case 0x16cc:
5307       element = EL_LIGHT_SWITCH;
5308       break;
5309
5310     case 0x16cd:
5311       element = EL_ENVELOPE_1;
5312       break;
5313
5314     default:
5315       if (element >= 0x0117 && element <= 0x036e)       /* (?) */
5316         element = EL_DIAMOND;
5317       else if (element >= 0x042d && element <= 0x0684)  /* (?) */
5318         element = EL_EMERALD;
5319       else if (element >= 0x157c && element <= 0x158b)
5320         element = EL_SAND;
5321       else if (element >= 0x1590 && element <= 0x159f)
5322         element = EL_DC_LANDMINE;
5323       else if (element >= 0x16bc && element <= 0x16cb)
5324         element = EL_INVISIBLE_SAND;
5325       else
5326       {
5327         Error(ERR_WARN, "unknown Diamond Caves element 0x%04x", element);
5328         element = EL_UNKNOWN;
5329       }
5330       break;
5331   }
5332
5333   return getMappedElement(element);
5334 }
5335
5336 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level,
5337                                        int nr)
5338 {
5339   byte header[DC_LEVEL_HEADER_SIZE];
5340   int envelope_size;
5341   int envelope_header_pos = 62;
5342   int envelope_content_pos = 94;
5343   int level_name_pos = 251;
5344   int level_author_pos = 292;
5345   int envelope_header_len;
5346   int envelope_content_len;
5347   int level_name_len;
5348   int level_author_len;
5349   int fieldx, fieldy;
5350   int num_yamyam_contents;
5351   int i, x, y;
5352
5353   getDecodedWord_DC(0, TRUE);           /* initialize DC2 decoding engine */
5354
5355   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
5356   {
5357     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5358
5359     header[i * 2 + 0] = header_word >> 8;
5360     header[i * 2 + 1] = header_word & 0xff;
5361   }
5362
5363   /* read some values from level header to check level decoding integrity */
5364   fieldx = header[6] | (header[7] << 8);
5365   fieldy = header[8] | (header[9] << 8);
5366   num_yamyam_contents = header[60] | (header[61] << 8);
5367
5368   /* do some simple sanity checks to ensure that level was correctly decoded */
5369   if (fieldx < 1 || fieldx > 256 ||
5370       fieldy < 1 || fieldy > 256 ||
5371       num_yamyam_contents < 1 || num_yamyam_contents > 8)
5372   {
5373     level->no_valid_file = TRUE;
5374
5375     Error(ERR_WARN, "cannot decode level from stream -- using empty level");
5376
5377     return;
5378   }
5379
5380   /* maximum envelope header size is 31 bytes */
5381   envelope_header_len   = header[envelope_header_pos];
5382   /* maximum envelope content size is 110 (156?) bytes */
5383   envelope_content_len  = header[envelope_content_pos];
5384
5385   /* maximum level title size is 40 bytes */
5386   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
5387   /* maximum level author size is 30 (51?) bytes */
5388   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
5389
5390   envelope_size = 0;
5391
5392   for (i = 0; i < envelope_header_len; i++)
5393     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5394       level->envelope[0].text[envelope_size++] =
5395         header[envelope_header_pos + 1 + i];
5396
5397   if (envelope_header_len > 0 && envelope_content_len > 0)
5398   {
5399     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5400       level->envelope[0].text[envelope_size++] = '\n';
5401     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5402       level->envelope[0].text[envelope_size++] = '\n';
5403   }
5404
5405   for (i = 0; i < envelope_content_len; i++)
5406     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
5407       level->envelope[0].text[envelope_size++] =
5408         header[envelope_content_pos + 1 + i];
5409
5410   level->envelope[0].text[envelope_size] = '\0';
5411
5412   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
5413   level->envelope[0].ysize = 10;
5414   level->envelope[0].autowrap = TRUE;
5415   level->envelope[0].centered = TRUE;
5416
5417   for (i = 0; i < level_name_len; i++)
5418     level->name[i] = header[level_name_pos + 1 + i];
5419   level->name[level_name_len] = '\0';
5420
5421   for (i = 0; i < level_author_len; i++)
5422     level->author[i] = header[level_author_pos + 1 + i];
5423   level->author[level_author_len] = '\0';
5424
5425   num_yamyam_contents = header[60] | (header[61] << 8);
5426   level->num_yamyam_contents =
5427     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
5428
5429   for (i = 0; i < num_yamyam_contents; i++)
5430   {
5431     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
5432     {
5433       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5434       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5435
5436       if (i < MAX_ELEMENT_CONTENTS)
5437         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
5438     }
5439   }
5440
5441   fieldx = header[6] | (header[7] << 8);
5442   fieldy = header[8] | (header[9] << 8);
5443   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
5444   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
5445
5446   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
5447   {
5448     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
5449     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
5450
5451     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
5452       level->field[x][y] = getMappedElement_DC(element_dc);
5453   }
5454
5455   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
5456   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
5457   level->field[x][y] = EL_PLAYER_1;
5458
5459   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
5460   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
5461   level->field[x][y] = EL_PLAYER_2;
5462
5463   level->gems_needed            = header[18] | (header[19] << 8);
5464
5465   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
5466   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
5467   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
5468   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
5469   level->score[SC_NUT]          = header[28] | (header[29] << 8);
5470   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
5471   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
5472   level->score[SC_BUG]          = header[34] | (header[35] << 8);
5473   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
5474   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
5475   level->score[SC_KEY]          = header[40] | (header[41] << 8);
5476   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
5477
5478   level->time                   = header[44] | (header[45] << 8);
5479
5480   level->amoeba_speed           = header[46] | (header[47] << 8);
5481   level->time_light             = header[48] | (header[49] << 8);
5482   level->time_timegate          = header[50] | (header[51] << 8);
5483   level->time_wheel             = header[52] | (header[53] << 8);
5484   level->time_magic_wall        = header[54] | (header[55] << 8);
5485   level->extra_time             = header[56] | (header[57] << 8);
5486   level->shield_normal_time     = header[58] | (header[59] << 8);
5487
5488   /* Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
5489      can slip down from flat walls, like normal walls and steel walls */
5490   level->em_slippery_gems = TRUE;
5491 }
5492
5493 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
5494                                      struct LevelFileInfo *level_file_info,
5495                                      boolean level_info_only)
5496 {
5497   char *filename = level_file_info->filename;
5498   File *file;
5499   int num_magic_bytes = 8;
5500   char magic_bytes[num_magic_bytes + 1];
5501   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5502
5503   if (!(file = openFile(filename, MODE_READ)))
5504   {
5505     level->no_valid_file = TRUE;
5506
5507     if (!level_info_only)
5508       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5509
5510     return;
5511   }
5512
5513   // fseek(file, 0x0000, SEEK_SET);
5514
5515   if (level_file_info->packed)
5516   {
5517     /* read "magic bytes" from start of file */
5518     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
5519       magic_bytes[0] = '\0';
5520
5521     /* check "magic bytes" for correct file format */
5522     if (!strPrefix(magic_bytes, "DC2"))
5523     {
5524       level->no_valid_file = TRUE;
5525
5526       Error(ERR_WARN, "unknown DC level file '%s' -- using empty level",
5527             filename);
5528
5529       return;
5530     }
5531
5532     if (strPrefix(magic_bytes, "DC2Win95") ||
5533         strPrefix(magic_bytes, "DC2Win98"))
5534     {
5535       int position_first_level = 0x00fa;
5536       int extra_bytes = 4;
5537       int skip_bytes;
5538
5539       /* advance file stream to first level inside the level package */
5540       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
5541
5542       /* each block of level data is followed by block of non-level data */
5543       num_levels_to_skip *= 2;
5544
5545       /* at least skip header bytes, therefore use ">= 0" instead of "> 0" */
5546       while (num_levels_to_skip >= 0)
5547       {
5548         /* advance file stream to next level inside the level package */
5549         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
5550         {
5551           level->no_valid_file = TRUE;
5552
5553           Error(ERR_WARN, "cannot fseek in file '%s' -- using empty level",
5554                 filename);
5555
5556           return;
5557         }
5558
5559         /* skip apparently unused extra bytes following each level */
5560         ReadUnusedBytesFromFile(file, extra_bytes);
5561
5562         /* read size of next level in level package */
5563         skip_bytes = getFile32BitLE(file);
5564
5565         num_levels_to_skip--;
5566       }
5567     }
5568     else
5569     {
5570       level->no_valid_file = TRUE;
5571
5572       Error(ERR_WARN, "unknown DC2 level file '%s' -- using empty level",
5573             filename);
5574
5575       return;
5576     }
5577   }
5578
5579   LoadLevelFromFileStream_DC(file, level, level_file_info->nr);
5580
5581   closeFile(file);
5582 }
5583
5584
5585 /* ------------------------------------------------------------------------- */
5586 /* functions for loading SB level                                            */
5587 /* ------------------------------------------------------------------------- */
5588
5589 int getMappedElement_SB(int element_ascii, boolean use_ces)
5590 {
5591   static struct
5592   {
5593     int ascii;
5594     int sb;
5595     int ce;
5596   }
5597   sb_element_mapping[] =
5598   {
5599     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  /* floor (space) */
5600     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  /* wall */
5601     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  /* player */
5602     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  /* box */
5603     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  /* goal square */
5604     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  /* box on goal square */
5605     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  /* player on goal square */
5606     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  /* floor beyond border */
5607
5608     { 0,   -1,                      -1          },
5609   };
5610
5611   int i;
5612
5613   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
5614     if (element_ascii == sb_element_mapping[i].ascii)
5615       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
5616
5617   return EL_UNDEFINED;
5618 }
5619
5620 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
5621                                      struct LevelFileInfo *level_file_info,
5622                                      boolean level_info_only)
5623 {
5624   char *filename = level_file_info->filename;
5625   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
5626   char last_comment[MAX_LINE_LEN];
5627   char level_name[MAX_LINE_LEN];
5628   char *line_ptr;
5629   File *file;
5630   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
5631   boolean read_continued_line = FALSE;
5632   boolean reading_playfield = FALSE;
5633   boolean got_valid_playfield_line = FALSE;
5634   boolean invalid_playfield_char = FALSE;
5635   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
5636   int file_level_nr = 0;
5637   int line_nr = 0;
5638   int x = 0, y = 0;             /* initialized to make compilers happy */
5639
5640   last_comment[0] = '\0';
5641   level_name[0] = '\0';
5642
5643   if (!(file = openFile(filename, MODE_READ)))
5644   {
5645     level->no_valid_file = TRUE;
5646
5647     if (!level_info_only)
5648       Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5649
5650     return;
5651   }
5652
5653   while (!checkEndOfFile(file))
5654   {
5655     /* level successfully read, but next level may follow here */
5656     if (!got_valid_playfield_line && reading_playfield)
5657     {
5658       /* read playfield from single level file -- skip remaining file */
5659       if (!level_file_info->packed)
5660         break;
5661
5662       if (file_level_nr >= num_levels_to_skip)
5663         break;
5664
5665       file_level_nr++;
5666
5667       last_comment[0] = '\0';
5668       level_name[0] = '\0';
5669
5670       reading_playfield = FALSE;
5671     }
5672
5673     got_valid_playfield_line = FALSE;
5674
5675     /* read next line of input file */
5676     if (!getStringFromFile(file, line, MAX_LINE_LEN))
5677       break;
5678
5679     /* check if line was completely read and is terminated by line break */
5680     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
5681       line_nr++;
5682
5683     /* cut trailing line break (this can be newline and/or carriage return) */
5684     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
5685       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
5686         *line_ptr = '\0';
5687
5688     /* copy raw input line for later use (mainly debugging output) */
5689     strcpy(line_raw, line);
5690
5691     if (read_continued_line)
5692     {
5693       /* append new line to existing line, if there is enough space */
5694       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
5695         strcat(previous_line, line_ptr);
5696
5697       strcpy(line, previous_line);      /* copy storage buffer to line */
5698
5699       read_continued_line = FALSE;
5700     }
5701
5702     /* if the last character is '\', continue at next line */
5703     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
5704     {
5705       line[strlen(line) - 1] = '\0';    /* cut off trailing backslash */
5706       strcpy(previous_line, line);      /* copy line to storage buffer */
5707
5708       read_continued_line = TRUE;
5709
5710       continue;
5711     }
5712
5713     /* skip empty lines */
5714     if (line[0] == '\0')
5715       continue;
5716
5717     /* extract comment text from comment line */
5718     if (line[0] == ';')
5719     {
5720       for (line_ptr = line; *line_ptr; line_ptr++)
5721         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
5722           break;
5723
5724       strcpy(last_comment, line_ptr);
5725
5726       continue;
5727     }
5728
5729     /* extract level title text from line containing level title */
5730     if (line[0] == '\'')
5731     {
5732       strcpy(level_name, &line[1]);
5733
5734       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
5735         level_name[strlen(level_name) - 1] = '\0';
5736
5737       continue;
5738     }
5739
5740     /* skip lines containing only spaces (or empty lines) */
5741     for (line_ptr = line; *line_ptr; line_ptr++)
5742       if (*line_ptr != ' ')
5743         break;
5744     if (*line_ptr == '\0')
5745       continue;
5746
5747     /* at this point, we have found a line containing part of a playfield */
5748
5749     got_valid_playfield_line = TRUE;
5750
5751     if (!reading_playfield)
5752     {
5753       reading_playfield = TRUE;
5754       invalid_playfield_char = FALSE;
5755
5756       for (x = 0; x < MAX_LEV_FIELDX; x++)
5757         for (y = 0; y < MAX_LEV_FIELDY; y++)
5758           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
5759
5760       level->fieldx = 0;
5761       level->fieldy = 0;
5762
5763       /* start with topmost tile row */
5764       y = 0;
5765     }
5766
5767     /* skip playfield line if larger row than allowed */
5768     if (y >= MAX_LEV_FIELDY)
5769       continue;
5770
5771     /* start with leftmost tile column */
5772     x = 0;
5773
5774     /* read playfield elements from line */
5775     for (line_ptr = line; *line_ptr; line_ptr++)
5776     {
5777       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
5778
5779       /* stop parsing playfield line if larger column than allowed */
5780       if (x >= MAX_LEV_FIELDX)
5781         break;
5782
5783       if (mapped_sb_element == EL_UNDEFINED)
5784       {
5785         invalid_playfield_char = TRUE;
5786
5787         break;
5788       }
5789
5790       level->field[x][y] = mapped_sb_element;
5791
5792       /* continue with next tile column */
5793       x++;
5794
5795       level->fieldx = MAX(x, level->fieldx);
5796     }
5797
5798     if (invalid_playfield_char)
5799     {
5800       /* if first playfield line, treat invalid lines as comment lines */
5801       if (y == 0)
5802         reading_playfield = FALSE;
5803
5804       continue;
5805     }
5806
5807     /* continue with next tile row */
5808     y++;
5809   }
5810
5811   closeFile(file);
5812
5813   level->fieldy = y;
5814
5815   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
5816   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
5817
5818   if (!reading_playfield)
5819   {
5820     level->no_valid_file = TRUE;
5821
5822     Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
5823
5824     return;
5825   }
5826
5827   if (*level_name != '\0')
5828   {
5829     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
5830     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5831   }
5832   else if (*last_comment != '\0')
5833   {
5834     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
5835     level->name[MAX_LEVEL_NAME_LEN] = '\0';
5836   }
5837   else
5838   {
5839     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
5840   }
5841
5842   /* set all empty fields beyond the border walls to invisible steel wall */
5843   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
5844   {
5845     if ((x == 0 || x == level->fieldx - 1 ||
5846          y == 0 || y == level->fieldy - 1) &&
5847         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
5848       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
5849                      level->field, level->fieldx, level->fieldy);
5850   }
5851
5852   /* set special level settings for Sokoban levels */
5853
5854   level->time = 0;
5855   level->use_step_counter = TRUE;
5856
5857   if (load_xsb_to_ces)
5858   {
5859     /* special global settings can now be set in level template */
5860
5861     level->use_custom_template = TRUE;
5862   }
5863 }
5864
5865
5866 /* ------------------------------------------------------------------------- */
5867 /* functions for handling native levels                                      */
5868 /* ------------------------------------------------------------------------- */
5869
5870 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
5871                                      struct LevelFileInfo *level_file_info,
5872                                      boolean level_info_only)
5873 {
5874   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
5875     level->no_valid_file = TRUE;
5876 }
5877
5878 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
5879                                      struct LevelFileInfo *level_file_info,
5880                                      boolean level_info_only)
5881 {
5882   int pos = 0;
5883
5884   /* determine position of requested level inside level package */
5885   if (level_file_info->packed)
5886     pos = level_file_info->nr - leveldir_current->first_level;
5887
5888   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
5889     level->no_valid_file = TRUE;
5890 }
5891
5892 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
5893 {
5894   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5895     CopyNativeLevel_RND_to_EM(level);
5896   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5897     CopyNativeLevel_RND_to_SP(level);
5898 }
5899
5900 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
5901 {
5902   if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
5903     CopyNativeLevel_EM_to_RND(level);
5904   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5905     CopyNativeLevel_SP_to_RND(level);
5906 }
5907
5908 void SaveNativeLevel(struct LevelInfo *level)
5909 {
5910   if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
5911   {
5912     char *basename = getSingleLevelBasenameExt(level->file_info.nr, "sp");
5913     char *filename = getLevelFilenameFromBasename(basename);
5914
5915     CopyNativeLevel_RND_to_SP(level);
5916     CopyNativeTape_RND_to_SP(level);
5917
5918     SaveNativeLevel_SP(filename);
5919   }
5920 }
5921
5922
5923 /* ------------------------------------------------------------------------- */
5924 /* functions for loading generic level                                       */
5925 /* ------------------------------------------------------------------------- */
5926
5927 static void LoadLevelFromFileInfo(struct LevelInfo *level,
5928                                   struct LevelFileInfo *level_file_info,
5929                                   boolean level_info_only)
5930 {
5931   /* always start with reliable default values */
5932   setLevelInfoToDefaults(level, level_info_only, TRUE);
5933
5934   switch (level_file_info->type)
5935   {
5936     case LEVEL_FILE_TYPE_RND:
5937       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5938       break;
5939
5940     case LEVEL_FILE_TYPE_EM:
5941       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
5942       level->game_engine_type = GAME_ENGINE_TYPE_EM;
5943       break;
5944
5945     case LEVEL_FILE_TYPE_SP:
5946       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
5947       level->game_engine_type = GAME_ENGINE_TYPE_SP;
5948       break;
5949
5950     case LEVEL_FILE_TYPE_DC:
5951       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
5952       break;
5953
5954     case LEVEL_FILE_TYPE_SB:
5955       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
5956       break;
5957
5958     default:
5959       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
5960       break;
5961   }
5962
5963   /* if level file is invalid, restore level structure to default values */
5964   if (level->no_valid_file)
5965     setLevelInfoToDefaults(level, level_info_only, FALSE);
5966
5967   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
5968     level->game_engine_type = GAME_ENGINE_TYPE_RND;
5969
5970   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
5971     CopyNativeLevel_Native_to_RND(level);
5972 }
5973
5974 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
5975 {
5976   static struct LevelFileInfo level_file_info;
5977
5978   /* always start with reliable default values */
5979   setFileInfoToDefaults(&level_file_info);
5980
5981   level_file_info.nr = 0;                       /* unknown level number */
5982   level_file_info.type = LEVEL_FILE_TYPE_RND;   /* no others supported yet */
5983   level_file_info.filename = filename;
5984
5985   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
5986 }
5987
5988 static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
5989 {
5990   int i, j;
5991
5992   if (leveldir_current == NULL)         /* only when dumping level */
5993     return;
5994
5995   /* all engine modifications also valid for levels which use latest engine */
5996   if (level->game_version < VERSION_IDENT(3,2,0,5))
5997   {
5998     /* time bonus score was given for 10 s instead of 1 s before 3.2.0-5 */
5999     level->score[SC_TIME_BONUS] /= 10;
6000   }
6001
6002   if (leveldir_current->latest_engine)
6003   {
6004     /* ---------- use latest game engine ----------------------------------- */
6005
6006     /* For all levels which are forced to use the latest game engine version
6007        (normally all but user contributed, private and undefined levels), set
6008        the game engine version to the actual version; this allows for actual
6009        corrections in the game engine to take effect for existing, converted
6010        levels (from "classic" or other existing games) to make the emulation
6011        of the corresponding game more accurate, while (hopefully) not breaking
6012        existing levels created from other players. */
6013
6014     level->game_version = GAME_VERSION_ACTUAL;
6015
6016     /* Set special EM style gems behaviour: EM style gems slip down from
6017        normal, steel and growing wall. As this is a more fundamental change,
6018        it seems better to set the default behaviour to "off" (as it is more
6019        natural) and make it configurable in the level editor (as a property
6020        of gem style elements). Already existing converted levels (neither
6021        private nor contributed levels) are changed to the new behaviour. */
6022
6023     if (level->file_version < FILE_VERSION_2_0)
6024       level->em_slippery_gems = TRUE;
6025
6026     return;
6027   }
6028
6029   /* ---------- use game engine the level was created with ----------------- */
6030
6031   /* For all levels which are not forced to use the latest game engine
6032      version (normally user contributed, private and undefined levels),
6033      use the version of the game engine the levels were created for.
6034
6035      Since 2.0.1, the game engine version is now directly stored
6036      in the level file (chunk "VERS"), so there is no need anymore
6037      to set the game version from the file version (except for old,
6038      pre-2.0 levels, where the game version is still taken from the
6039      file format version used to store the level -- see above). */
6040
6041   /* player was faster than enemies in 1.0.0 and before */
6042   if (level->file_version == FILE_VERSION_1_0)
6043     for (i = 0; i < MAX_PLAYERS; i++)
6044       level->initial_player_stepsize[i] = STEPSIZE_FAST;
6045
6046   /* default behaviour for EM style gems was "slippery" only in 2.0.1 */
6047   if (level->game_version == VERSION_IDENT(2,0,1,0))
6048     level->em_slippery_gems = TRUE;
6049
6050   /* springs could be pushed over pits before (pre-release version) 2.2.0 */
6051   if (level->game_version < VERSION_IDENT(2,2,0,0))
6052     level->use_spring_bug = TRUE;
6053
6054   if (level->game_version < VERSION_IDENT(3,2,0,5))
6055   {
6056     /* time orb caused limited time in endless time levels before 3.2.0-5 */
6057     level->use_time_orb_bug = TRUE;
6058
6059     /* default behaviour for snapping was "no snap delay" before 3.2.0-5 */
6060     level->block_snap_field = FALSE;
6061
6062     /* extra time score was same value as time left score before 3.2.0-5 */
6063     level->extra_time_score = level->score[SC_TIME_BONUS];
6064   }
6065
6066   if (level->game_version < VERSION_IDENT(3,2,0,7))
6067   {
6068     /* default behaviour for snapping was "not continuous" before 3.2.0-7 */
6069     level->continuous_snapping = FALSE;
6070   }
6071
6072   /* only few elements were able to actively move into acid before 3.1.0 */
6073   /* trigger settings did not exist before 3.1.0; set to default "any" */
6074   if (level->game_version < VERSION_IDENT(3,1,0,0))
6075   {
6076     /* correct "can move into acid" settings (all zero in old levels) */
6077
6078     level->can_move_into_acid_bits = 0; /* nothing can move into acid */
6079     level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
6080
6081     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
6082     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
6083     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
6084     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
6085
6086     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6087       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
6088
6089     /* correct trigger settings (stored as zero == "none" in old levels) */
6090
6091     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6092     {
6093       int element = EL_CUSTOM_START + i;
6094       struct ElementInfo *ei = &element_info[element];
6095
6096       for (j = 0; j < ei->num_change_pages; j++)
6097       {
6098         struct ElementChangeInfo *change = &ei->change_page[j];
6099
6100         change->trigger_player = CH_PLAYER_ANY;
6101         change->trigger_page = CH_PAGE_ANY;
6102       }
6103     }
6104   }
6105
6106   /* try to detect and fix "Snake Bite" levels, which are broken with 3.2.0 */
6107   {
6108     int element = EL_CUSTOM_256;
6109     struct ElementInfo *ei = &element_info[element];
6110     struct ElementChangeInfo *change = &ei->change_page[0];
6111
6112     /* This is needed to fix a problem that was caused by a bugfix in function
6113        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
6114        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
6115        not replace walkable elements, but instead just placed the player on it,
6116        without placing the Sokoban field under the player). Unfortunately, this
6117        breaks "Snake Bite" style levels when the snake is halfway through a door
6118        that just closes (the snake head is still alive and can be moved in this
6119        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
6120        player (without Sokoban element) which then gets killed as designed). */
6121
6122     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
6123          strncmp(ei->description, "pause b4 death", 14) == 0) &&
6124         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
6125       change->target_element = EL_PLAYER_1;
6126   }
6127
6128   /* try to detect and fix "Zelda" style levels, which are broken with 3.2.5 */
6129   if (level->game_version < VERSION_IDENT(3,2,5,0))
6130   {
6131     /* This is needed to fix a problem that was caused by a bugfix in function
6132        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
6133        corrects the behaviour when a custom element changes to another custom
6134        element with a higher element number that has change actions defined.
6135        Normally, only one change per frame is allowed for custom elements.
6136        Therefore, it is checked if a custom element already changed in the
6137        current frame; if it did, subsequent changes are suppressed.
6138        Unfortunately, this is only checked for element changes, but not for
6139        change actions, which are still executed. As the function above loops
6140        through all custom elements from lower to higher, an element change
6141        resulting in a lower CE number won't be checked again, while a target
6142        element with a higher number will also be checked, and potential change
6143        actions will get executed for this CE, too (which is wrong), while
6144        further changes are ignored (which is correct). As this bugfix breaks
6145        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
6146        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
6147        behaviour for existing levels and tapes that make use of this bug */
6148
6149     level->use_action_after_change_bug = TRUE;
6150   }
6151
6152   /* not centering level after relocating player was default only in 3.2.3 */
6153   if (level->game_version == VERSION_IDENT(3,2,3,0))    /* (no pre-releases) */
6154     level->shifted_relocation = TRUE;
6155
6156   /* EM style elements always chain-exploded in R'n'D engine before 3.2.6 */
6157   if (level->game_version < VERSION_IDENT(3,2,6,0))
6158     level->em_explodes_by_fire = TRUE;
6159 }
6160
6161 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6162 {
6163   int i, j, x, y;
6164
6165   /* map custom element change events that have changed in newer versions
6166      (these following values were accidentally changed in version 3.0.1)
6167      (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4') */
6168   if (level->game_version <= VERSION_IDENT(3,0,0,0))
6169   {
6170     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6171     {
6172       int element = EL_CUSTOM_START + i;
6173
6174       /* order of checking and copying events to be mapped is important */
6175       /* (do not change the start and end value -- they are constant) */
6176       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
6177       {
6178         if (HAS_CHANGE_EVENT(element, j - 2))
6179         {
6180           SET_CHANGE_EVENT(element, j - 2, FALSE);
6181           SET_CHANGE_EVENT(element, j, TRUE);
6182         }
6183       }
6184
6185       /* order of checking and copying events to be mapped is important */
6186       /* (do not change the start and end value -- they are constant) */
6187       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
6188       {
6189         if (HAS_CHANGE_EVENT(element, j - 1))
6190         {
6191           SET_CHANGE_EVENT(element, j - 1, FALSE);
6192           SET_CHANGE_EVENT(element, j, TRUE);
6193         }
6194       }
6195     }
6196   }
6197
6198   /* initialize "can_change" field for old levels with only one change page */
6199   if (level->game_version <= VERSION_IDENT(3,0,2,0))
6200   {
6201     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6202     {
6203       int element = EL_CUSTOM_START + i;
6204
6205       if (CAN_CHANGE(element))
6206         element_info[element].change->can_change = TRUE;
6207     }
6208   }
6209
6210   /* correct custom element values (for old levels without these options) */
6211   if (level->game_version < VERSION_IDENT(3,1,1,0))
6212   {
6213     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6214     {
6215       int element = EL_CUSTOM_START + i;
6216       struct ElementInfo *ei = &element_info[element];
6217
6218       if (ei->access_direction == MV_NO_DIRECTION)
6219         ei->access_direction = MV_ALL_DIRECTIONS;
6220     }
6221   }
6222
6223   /* correct custom element values (fix invalid values for all versions) */
6224   if (1)
6225   {
6226     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6227     {
6228       int element = EL_CUSTOM_START + i;
6229       struct ElementInfo *ei = &element_info[element];
6230
6231       for (j = 0; j < ei->num_change_pages; j++)
6232       {
6233         struct ElementChangeInfo *change = &ei->change_page[j];
6234
6235         if (change->trigger_player == CH_PLAYER_NONE)
6236           change->trigger_player = CH_PLAYER_ANY;
6237
6238         if (change->trigger_side == CH_SIDE_NONE)
6239           change->trigger_side = CH_SIDE_ANY;
6240       }
6241     }
6242   }
6243
6244   /* initialize "can_explode" field for old levels which did not store this */
6245   /* !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!! */
6246   if (level->game_version <= VERSION_IDENT(3,1,0,0))
6247   {
6248     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6249     {
6250       int element = EL_CUSTOM_START + i;
6251
6252       if (EXPLODES_1X1_OLD(element))
6253         element_info[element].explosion_type = EXPLODES_1X1;
6254
6255       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
6256                                              EXPLODES_SMASHED(element) ||
6257                                              EXPLODES_IMPACT(element)));
6258     }
6259   }
6260
6261   /* correct previously hard-coded move delay values for maze runner style */
6262   if (level->game_version < VERSION_IDENT(3,1,1,0))
6263   {
6264     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6265     {
6266       int element = EL_CUSTOM_START + i;
6267
6268       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
6269       {
6270         /* previously hard-coded and therefore ignored */
6271         element_info[element].move_delay_fixed = 9;
6272         element_info[element].move_delay_random = 0;
6273       }
6274     }
6275   }
6276
6277   /* map elements that have changed in newer versions */
6278   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
6279                                                     level->game_version);
6280   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6281     for (x = 0; x < 3; x++)
6282       for (y = 0; y < 3; y++)
6283         level->yamyam_content[i].e[x][y] =
6284           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
6285                                     level->game_version);
6286
6287   /* initialize element properties for level editor etc. */
6288   InitElementPropertiesEngine(level->game_version);
6289   InitElementPropertiesAfterLoading(level->game_version);
6290   InitElementPropertiesGfxElement();
6291 }
6292
6293 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6294 {
6295   int x, y;
6296
6297   /* map elements that have changed in newer versions */
6298   for (y = 0; y < level->fieldy; y++)
6299     for (x = 0; x < level->fieldx; x++)
6300       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6301                                                      level->game_version);
6302
6303   /* clear unused playfield data (nicer if level gets resized in editor) */
6304   for (x = 0; x < MAX_LEV_FIELDX; x++)
6305     for (y = 0; y < MAX_LEV_FIELDY; y++)
6306       if (x >= level->fieldx || y >= level->fieldy)
6307         level->field[x][y] = EL_EMPTY;
6308
6309   /* copy elements to runtime playfield array */
6310   for (x = 0; x < MAX_LEV_FIELDX; x++)
6311     for (y = 0; y < MAX_LEV_FIELDY; y++)
6312       Feld[x][y] = level->field[x][y];
6313
6314   /* initialize level size variables for faster access */
6315   lev_fieldx = level->fieldx;
6316   lev_fieldy = level->fieldy;
6317
6318   /* determine border element for this level */
6319   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6320     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6321   else
6322     SetBorderElement();
6323 }
6324
6325 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6326 {
6327   struct LevelFileInfo *level_file_info = &level->file_info;
6328
6329   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6330     CopyNativeLevel_RND_to_Native(level);
6331 }
6332
6333 void LoadLevelTemplate(int nr)
6334 {
6335   char *filename;
6336
6337   setLevelFileInfo(&level_template.file_info, nr);
6338   filename = level_template.file_info.filename;
6339
6340   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6341
6342   LoadLevel_InitVersion(&level_template, filename);
6343   LoadLevel_InitElements(&level_template, filename);
6344
6345   ActivateLevelTemplate();
6346 }
6347
6348 void LoadLevel(int nr)
6349 {
6350   char *filename;
6351
6352   setLevelFileInfo(&level.file_info, nr);
6353   filename = level.file_info.filename;
6354
6355   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6356
6357   if (level.use_custom_template)
6358     LoadLevelTemplate(-1);
6359
6360   LoadLevel_InitVersion(&level, filename);
6361   LoadLevel_InitElements(&level, filename);
6362   LoadLevel_InitPlayfield(&level, filename);
6363
6364   LoadLevel_InitNativeEngines(&level, filename);
6365 }
6366
6367 void LoadLevelInfoOnly(int nr)
6368 {
6369   setLevelFileInfo(&level.file_info, nr);
6370
6371   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6372 }
6373
6374 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6375 {
6376   int chunk_size = 0;
6377
6378   chunk_size += putFileVersion(file, level->file_version);
6379   chunk_size += putFileVersion(file, level->game_version);
6380
6381   return chunk_size;
6382 }
6383
6384 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6385 {
6386   int chunk_size = 0;
6387
6388   chunk_size += putFile16BitBE(file, level->creation_date.year);
6389   chunk_size += putFile8Bit(file,    level->creation_date.month);
6390   chunk_size += putFile8Bit(file,    level->creation_date.day);
6391
6392   return chunk_size;
6393 }
6394
6395 #if ENABLE_HISTORIC_CHUNKS
6396 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6397 {
6398   int i, x, y;
6399
6400   putFile8Bit(file, level->fieldx);
6401   putFile8Bit(file, level->fieldy);
6402
6403   putFile16BitBE(file, level->time);
6404   putFile16BitBE(file, level->gems_needed);
6405
6406   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6407     putFile8Bit(file, level->name[i]);
6408
6409   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6410     putFile8Bit(file, level->score[i]);
6411
6412   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6413     for (y = 0; y < 3; y++)
6414       for (x = 0; x < 3; x++)
6415         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6416                            level->yamyam_content[i].e[x][y]));
6417   putFile8Bit(file, level->amoeba_speed);
6418   putFile8Bit(file, level->time_magic_wall);
6419   putFile8Bit(file, level->time_wheel);
6420   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6421                      level->amoeba_content));
6422   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6423   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6424   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6425   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6426
6427   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6428
6429   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6430   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6431   putFile32BitBE(file, level->can_move_into_acid_bits);
6432   putFile8Bit(file, level->dont_collide_with_bits);
6433
6434   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6435   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6436
6437   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6438   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6439   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6440
6441   putFile8Bit(file, level->game_engine_type);
6442
6443   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6444 }
6445 #endif
6446
6447 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6448 {
6449   int chunk_size = 0;
6450   int i;
6451
6452   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6453     chunk_size += putFile8Bit(file, level->name[i]);
6454
6455   return chunk_size;
6456 }
6457
6458 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6459 {
6460   int chunk_size = 0;
6461   int i;
6462
6463   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6464     chunk_size += putFile8Bit(file, level->author[i]);
6465
6466   return chunk_size;
6467 }
6468
6469 #if ENABLE_HISTORIC_CHUNKS
6470 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6471 {
6472   int chunk_size = 0;
6473   int x, y;
6474
6475   for (y = 0; y < level->fieldy; y++) 
6476     for (x = 0; x < level->fieldx; x++) 
6477       if (level->encoding_16bit_field)
6478         chunk_size += putFile16BitBE(file, level->field[x][y]);
6479       else
6480         chunk_size += putFile8Bit(file, level->field[x][y]);
6481
6482   return chunk_size;
6483 }
6484 #endif
6485
6486 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6487 {
6488   int chunk_size = 0;
6489   int x, y;
6490
6491   for (y = 0; y < level->fieldy; y++) 
6492     for (x = 0; x < level->fieldx; x++) 
6493       chunk_size += putFile16BitBE(file, level->field[x][y]);
6494
6495   return chunk_size;
6496 }
6497
6498 #if ENABLE_HISTORIC_CHUNKS
6499 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6500 {
6501   int i, x, y;
6502
6503   putFile8Bit(file, EL_YAMYAM);
6504   putFile8Bit(file, level->num_yamyam_contents);
6505   putFile8Bit(file, 0);
6506   putFile8Bit(file, 0);
6507
6508   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6509     for (y = 0; y < 3; y++)
6510       for (x = 0; x < 3; x++)
6511         if (level->encoding_16bit_field)
6512           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6513         else
6514           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6515 }
6516 #endif
6517
6518 #if ENABLE_HISTORIC_CHUNKS
6519 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6520 {
6521   int i, x, y;
6522   int num_contents, content_xsize, content_ysize;
6523   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6524
6525   if (element == EL_YAMYAM)
6526   {
6527     num_contents = level->num_yamyam_contents;
6528     content_xsize = 3;
6529     content_ysize = 3;
6530
6531     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6532       for (y = 0; y < 3; y++)
6533         for (x = 0; x < 3; x++)
6534           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6535   }
6536   else if (element == EL_BD_AMOEBA)
6537   {
6538     num_contents = 1;
6539     content_xsize = 1;
6540     content_ysize = 1;
6541
6542     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6543       for (y = 0; y < 3; y++)
6544         for (x = 0; x < 3; x++)
6545           content_array[i][x][y] = EL_EMPTY;
6546     content_array[0][0][0] = level->amoeba_content;
6547   }
6548   else
6549   {
6550     /* chunk header already written -- write empty chunk data */
6551     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6552
6553     Error(ERR_WARN, "cannot save content for element '%d'", element);
6554     return;
6555   }
6556
6557   putFile16BitBE(file, element);
6558   putFile8Bit(file, num_contents);
6559   putFile8Bit(file, content_xsize);
6560   putFile8Bit(file, content_ysize);
6561
6562   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6563
6564   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6565     for (y = 0; y < 3; y++)
6566       for (x = 0; x < 3; x++)
6567         putFile16BitBE(file, content_array[i][x][y]);
6568 }
6569 #endif
6570
6571 #if ENABLE_HISTORIC_CHUNKS
6572 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6573 {
6574   int envelope_nr = element - EL_ENVELOPE_1;
6575   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6576   int chunk_size = 0;
6577   int i;
6578
6579   chunk_size += putFile16BitBE(file, element);
6580   chunk_size += putFile16BitBE(file, envelope_len);
6581   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6582   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6583
6584   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6585   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6586
6587   for (i = 0; i < envelope_len; i++)
6588     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6589
6590   return chunk_size;
6591 }
6592 #endif
6593
6594 #if ENABLE_HISTORIC_CHUNKS
6595 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6596                            int num_changed_custom_elements)
6597 {
6598   int i, check = 0;
6599
6600   putFile16BitBE(file, num_changed_custom_elements);
6601
6602   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6603   {
6604     int element = EL_CUSTOM_START + i;
6605
6606     struct ElementInfo *ei = &element_info[element];
6607
6608     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6609     {
6610       if (check < num_changed_custom_elements)
6611       {
6612         putFile16BitBE(file, element);
6613         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6614       }
6615
6616       check++;
6617     }
6618   }
6619
6620   if (check != num_changed_custom_elements)     /* should not happen */
6621     Error(ERR_WARN, "inconsistent number of custom element properties");
6622 }
6623 #endif
6624
6625 #if ENABLE_HISTORIC_CHUNKS
6626 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6627                            int num_changed_custom_elements)
6628 {
6629   int i, check = 0;
6630
6631   putFile16BitBE(file, num_changed_custom_elements);
6632
6633   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6634   {
6635     int element = EL_CUSTOM_START + i;
6636
6637     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6638     {
6639       if (check < num_changed_custom_elements)
6640       {
6641         putFile16BitBE(file, element);
6642         putFile16BitBE(file, element_info[element].change->target_element);
6643       }
6644
6645       check++;
6646     }
6647   }
6648
6649   if (check != num_changed_custom_elements)     /* should not happen */
6650     Error(ERR_WARN, "inconsistent number of custom target elements");
6651 }
6652 #endif
6653
6654 #if ENABLE_HISTORIC_CHUNKS
6655 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6656                            int num_changed_custom_elements)
6657 {
6658   int i, j, x, y, check = 0;
6659
6660   putFile16BitBE(file, num_changed_custom_elements);
6661
6662   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6663   {
6664     int element = EL_CUSTOM_START + i;
6665     struct ElementInfo *ei = &element_info[element];
6666
6667     if (ei->modified_settings)
6668     {
6669       if (check < num_changed_custom_elements)
6670       {
6671         putFile16BitBE(file, element);
6672
6673         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6674           putFile8Bit(file, ei->description[j]);
6675
6676         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6677
6678         /* some free bytes for future properties and padding */
6679         WriteUnusedBytesToFile(file, 7);
6680
6681         putFile8Bit(file, ei->use_gfx_element);
6682         putFile16BitBE(file, ei->gfx_element_initial);
6683
6684         putFile8Bit(file, ei->collect_score_initial);
6685         putFile8Bit(file, ei->collect_count_initial);
6686
6687         putFile16BitBE(file, ei->push_delay_fixed);
6688         putFile16BitBE(file, ei->push_delay_random);
6689         putFile16BitBE(file, ei->move_delay_fixed);
6690         putFile16BitBE(file, ei->move_delay_random);
6691
6692         putFile16BitBE(file, ei->move_pattern);
6693         putFile8Bit(file, ei->move_direction_initial);
6694         putFile8Bit(file, ei->move_stepsize);
6695
6696         for (y = 0; y < 3; y++)
6697           for (x = 0; x < 3; x++)
6698             putFile16BitBE(file, ei->content.e[x][y]);
6699
6700         putFile32BitBE(file, ei->change->events);
6701
6702         putFile16BitBE(file, ei->change->target_element);
6703
6704         putFile16BitBE(file, ei->change->delay_fixed);
6705         putFile16BitBE(file, ei->change->delay_random);
6706         putFile16BitBE(file, ei->change->delay_frames);
6707
6708         putFile16BitBE(file, ei->change->initial_trigger_element);
6709
6710         putFile8Bit(file, ei->change->explode);
6711         putFile8Bit(file, ei->change->use_target_content);
6712         putFile8Bit(file, ei->change->only_if_complete);
6713         putFile8Bit(file, ei->change->use_random_replace);
6714
6715         putFile8Bit(file, ei->change->random_percentage);
6716         putFile8Bit(file, ei->change->replace_when);
6717
6718         for (y = 0; y < 3; y++)
6719           for (x = 0; x < 3; x++)
6720             putFile16BitBE(file, ei->change->content.e[x][y]);
6721
6722         putFile8Bit(file, ei->slippery_type);
6723
6724         /* some free bytes for future properties and padding */
6725         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6726       }
6727
6728       check++;
6729     }
6730   }
6731
6732   if (check != num_changed_custom_elements)     /* should not happen */
6733     Error(ERR_WARN, "inconsistent number of custom element properties");
6734 }
6735 #endif
6736
6737 #if ENABLE_HISTORIC_CHUNKS
6738 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6739 {
6740   struct ElementInfo *ei = &element_info[element];
6741   int i, j, x, y;
6742
6743   /* ---------- custom element base property values (96 bytes) ------------- */
6744
6745   putFile16BitBE(file, element);
6746
6747   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6748     putFile8Bit(file, ei->description[i]);
6749
6750   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6751
6752   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
6753
6754   putFile8Bit(file, ei->num_change_pages);
6755
6756   putFile16BitBE(file, ei->ce_value_fixed_initial);
6757   putFile16BitBE(file, ei->ce_value_random_initial);
6758   putFile8Bit(file, ei->use_last_ce_value);
6759
6760   putFile8Bit(file, ei->use_gfx_element);
6761   putFile16BitBE(file, ei->gfx_element_initial);
6762
6763   putFile8Bit(file, ei->collect_score_initial);
6764   putFile8Bit(file, ei->collect_count_initial);
6765
6766   putFile8Bit(file, ei->drop_delay_fixed);
6767   putFile8Bit(file, ei->push_delay_fixed);
6768   putFile8Bit(file, ei->drop_delay_random);
6769   putFile8Bit(file, ei->push_delay_random);
6770   putFile16BitBE(file, ei->move_delay_fixed);
6771   putFile16BitBE(file, ei->move_delay_random);
6772
6773   /* bits 0 - 15 of "move_pattern" ... */
6774   putFile16BitBE(file, ei->move_pattern & 0xffff);
6775   putFile8Bit(file, ei->move_direction_initial);
6776   putFile8Bit(file, ei->move_stepsize);
6777
6778   putFile8Bit(file, ei->slippery_type);
6779
6780   for (y = 0; y < 3; y++)
6781     for (x = 0; x < 3; x++)
6782       putFile16BitBE(file, ei->content.e[x][y]);
6783
6784   putFile16BitBE(file, ei->move_enter_element);
6785   putFile16BitBE(file, ei->move_leave_element);
6786   putFile8Bit(file, ei->move_leave_type);
6787
6788   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6789   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6790
6791   putFile8Bit(file, ei->access_direction);
6792
6793   putFile8Bit(file, ei->explosion_delay);
6794   putFile8Bit(file, ei->ignition_delay);
6795   putFile8Bit(file, ei->explosion_type);
6796
6797   /* some free bytes for future custom property values and padding */
6798   WriteUnusedBytesToFile(file, 1);
6799
6800   /* ---------- change page property values (48 bytes) --------------------- */
6801
6802   for (i = 0; i < ei->num_change_pages; i++)
6803   {
6804     struct ElementChangeInfo *change = &ei->change_page[i];
6805     unsigned int event_bits;
6806
6807     /* bits 0 - 31 of "has_event[]" ... */
6808     event_bits = 0;
6809     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6810       if (change->has_event[j])
6811         event_bits |= (1 << j);
6812     putFile32BitBE(file, event_bits);
6813
6814     putFile16BitBE(file, change->target_element);
6815
6816     putFile16BitBE(file, change->delay_fixed);
6817     putFile16BitBE(file, change->delay_random);
6818     putFile16BitBE(file, change->delay_frames);
6819
6820     putFile16BitBE(file, change->initial_trigger_element);
6821
6822     putFile8Bit(file, change->explode);
6823     putFile8Bit(file, change->use_target_content);
6824     putFile8Bit(file, change->only_if_complete);
6825     putFile8Bit(file, change->use_random_replace);
6826
6827     putFile8Bit(file, change->random_percentage);
6828     putFile8Bit(file, change->replace_when);
6829
6830     for (y = 0; y < 3; y++)
6831       for (x = 0; x < 3; x++)
6832         putFile16BitBE(file, change->target_content.e[x][y]);
6833
6834     putFile8Bit(file, change->can_change);
6835
6836     putFile8Bit(file, change->trigger_side);
6837
6838     putFile8Bit(file, change->trigger_player);
6839     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
6840                        log_2(change->trigger_page)));
6841
6842     putFile8Bit(file, change->has_action);
6843     putFile8Bit(file, change->action_type);
6844     putFile8Bit(file, change->action_mode);
6845     putFile16BitBE(file, change->action_arg);
6846
6847     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
6848     event_bits = 0;
6849     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
6850       if (change->has_event[j])
6851         event_bits |= (1 << (j - 32));
6852     putFile8Bit(file, event_bits);
6853   }
6854 }
6855 #endif
6856
6857 #if ENABLE_HISTORIC_CHUNKS
6858 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
6859 {
6860   struct ElementInfo *ei = &element_info[element];
6861   struct ElementGroupInfo *group = ei->group;
6862   int i;
6863
6864   putFile16BitBE(file, element);
6865
6866   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6867     putFile8Bit(file, ei->description[i]);
6868
6869   putFile8Bit(file, group->num_elements);
6870
6871   putFile8Bit(file, ei->use_gfx_element);
6872   putFile16BitBE(file, ei->gfx_element_initial);
6873
6874   putFile8Bit(file, group->choice_mode);
6875
6876   /* some free bytes for future values and padding */
6877   WriteUnusedBytesToFile(file, 3);
6878
6879   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
6880     putFile16BitBE(file, group->element[i]);
6881 }
6882 #endif
6883
6884 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
6885                                 boolean write_element)
6886 {
6887   int save_type = entry->save_type;
6888   int data_type = entry->data_type;
6889   int conf_type = entry->conf_type;
6890   int byte_mask = conf_type & CONF_MASK_BYTES;
6891   int element = entry->element;
6892   int default_value = entry->default_value;
6893   int num_bytes = 0;
6894   boolean modified = FALSE;
6895
6896   if (byte_mask != CONF_MASK_MULTI_BYTES)
6897   {
6898     void *value_ptr = entry->value;
6899     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
6900                  *(int *)value_ptr);
6901
6902     /* check if any settings have been modified before saving them */
6903     if (value != default_value)
6904       modified = TRUE;
6905
6906     /* do not save if explicitly told or if unmodified default settings */
6907     if ((save_type == SAVE_CONF_NEVER) ||
6908         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6909       return 0;
6910
6911     if (write_element)
6912       num_bytes += putFile16BitBE(file, element);
6913
6914     num_bytes += putFile8Bit(file, conf_type);
6915     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
6916                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
6917                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
6918                   0);
6919   }
6920   else if (data_type == TYPE_STRING)
6921   {
6922     char *default_string = entry->default_string;
6923     char *string = (char *)(entry->value);
6924     int string_length = strlen(string);
6925     int i;
6926
6927     /* check if any settings have been modified before saving them */
6928     if (!strEqual(string, default_string))
6929       modified = TRUE;
6930
6931     /* do not save if explicitly told or if unmodified default settings */
6932     if ((save_type == SAVE_CONF_NEVER) ||
6933         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6934       return 0;
6935
6936     if (write_element)
6937       num_bytes += putFile16BitBE(file, element);
6938
6939     num_bytes += putFile8Bit(file, conf_type);
6940     num_bytes += putFile16BitBE(file, string_length);
6941
6942     for (i = 0; i < string_length; i++)
6943       num_bytes += putFile8Bit(file, string[i]);
6944   }
6945   else if (data_type == TYPE_ELEMENT_LIST)
6946   {
6947     int *element_array = (int *)(entry->value);
6948     int num_elements = *(int *)(entry->num_entities);
6949     int i;
6950
6951     /* check if any settings have been modified before saving them */
6952     for (i = 0; i < num_elements; i++)
6953       if (element_array[i] != default_value)
6954         modified = TRUE;
6955
6956     /* do not save if explicitly told or if unmodified default settings */
6957     if ((save_type == SAVE_CONF_NEVER) ||
6958         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6959       return 0;
6960
6961     if (write_element)
6962       num_bytes += putFile16BitBE(file, element);
6963
6964     num_bytes += putFile8Bit(file, conf_type);
6965     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
6966
6967     for (i = 0; i < num_elements; i++)
6968       num_bytes += putFile16BitBE(file, element_array[i]);
6969   }
6970   else if (data_type == TYPE_CONTENT_LIST)
6971   {
6972     struct Content *content = (struct Content *)(entry->value);
6973     int num_contents = *(int *)(entry->num_entities);
6974     int i, x, y;
6975
6976     /* check if any settings have been modified before saving them */
6977     for (i = 0; i < num_contents; i++)
6978       for (y = 0; y < 3; y++)
6979         for (x = 0; x < 3; x++)
6980           if (content[i].e[x][y] != default_value)
6981             modified = TRUE;
6982
6983     /* do not save if explicitly told or if unmodified default settings */
6984     if ((save_type == SAVE_CONF_NEVER) ||
6985         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
6986       return 0;
6987
6988     if (write_element)
6989       num_bytes += putFile16BitBE(file, element);
6990
6991     num_bytes += putFile8Bit(file, conf_type);
6992     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
6993
6994     for (i = 0; i < num_contents; i++)
6995       for (y = 0; y < 3; y++)
6996         for (x = 0; x < 3; x++)
6997           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
6998   }
6999
7000   return num_bytes;
7001 }
7002
7003 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7004 {
7005   int chunk_size = 0;
7006   int i;
7007
7008   li = *level;          /* copy level data into temporary buffer */
7009
7010   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7011     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7012
7013   return chunk_size;
7014 }
7015
7016 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7017 {
7018   int chunk_size = 0;
7019   int i;
7020
7021   li = *level;          /* copy level data into temporary buffer */
7022
7023   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7024     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7025
7026   return chunk_size;
7027 }
7028
7029 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7030 {
7031   int envelope_nr = element - EL_ENVELOPE_1;
7032   int chunk_size = 0;
7033   int i;
7034
7035   chunk_size += putFile16BitBE(file, element);
7036
7037   /* copy envelope data into temporary buffer */
7038   xx_envelope = level->envelope[envelope_nr];
7039
7040   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7041     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7042
7043   return chunk_size;
7044 }
7045
7046 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7047 {
7048   struct ElementInfo *ei = &element_info[element];
7049   int chunk_size = 0;
7050   int i, j;
7051
7052   chunk_size += putFile16BitBE(file, element);
7053
7054   xx_ei = *ei;          /* copy element data into temporary buffer */
7055
7056   /* set default description string for this specific element */
7057   strcpy(xx_default_description, getDefaultElementDescription(ei));
7058
7059   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7060     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7061
7062   for (i = 0; i < ei->num_change_pages; i++)
7063   {
7064     struct ElementChangeInfo *change = &ei->change_page[i];
7065
7066     xx_current_change_page = i;
7067
7068     xx_change = *change;        /* copy change data into temporary buffer */
7069
7070     resetEventBits();
7071     setEventBitsFromEventFlags(change);
7072
7073     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7074       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7075                                          FALSE);
7076   }
7077
7078   return chunk_size;
7079 }
7080
7081 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7082 {
7083   struct ElementInfo *ei = &element_info[element];
7084   struct ElementGroupInfo *group = ei->group;
7085   int chunk_size = 0;
7086   int i;
7087
7088   chunk_size += putFile16BitBE(file, element);
7089
7090   xx_ei = *ei;          /* copy element data into temporary buffer */
7091   xx_group = *group;    /* copy group data into temporary buffer */
7092
7093   /* set default description string for this specific element */
7094   strcpy(xx_default_description, getDefaultElementDescription(ei));
7095
7096   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7097     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7098
7099   return chunk_size;
7100 }
7101
7102 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7103                                   boolean save_as_template)
7104 {
7105   int chunk_size;
7106   int i;
7107   FILE *file;
7108
7109   if (!(file = fopen(filename, MODE_WRITE)))
7110   {
7111     Error(ERR_WARN, "cannot save level file '%s'", filename);
7112     return;
7113   }
7114
7115   level->file_version = FILE_VERSION_ACTUAL;
7116   level->game_version = GAME_VERSION_ACTUAL;
7117
7118   level->creation_date = getCurrentDate();
7119
7120   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7121   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7122
7123   chunk_size = SaveLevel_VERS(NULL, level);
7124   putFileChunkBE(file, "VERS", chunk_size);
7125   SaveLevel_VERS(file, level);
7126
7127   chunk_size = SaveLevel_DATE(NULL, level);
7128   putFileChunkBE(file, "DATE", chunk_size);
7129   SaveLevel_DATE(file, level);
7130
7131   chunk_size = SaveLevel_NAME(NULL, level);
7132   putFileChunkBE(file, "NAME", chunk_size);
7133   SaveLevel_NAME(file, level);
7134
7135   chunk_size = SaveLevel_AUTH(NULL, level);
7136   putFileChunkBE(file, "AUTH", chunk_size);
7137   SaveLevel_AUTH(file, level);
7138
7139   chunk_size = SaveLevel_INFO(NULL, level);
7140   putFileChunkBE(file, "INFO", chunk_size);
7141   SaveLevel_INFO(file, level);
7142
7143   chunk_size = SaveLevel_BODY(NULL, level);
7144   putFileChunkBE(file, "BODY", chunk_size);
7145   SaveLevel_BODY(file, level);
7146
7147   chunk_size = SaveLevel_ELEM(NULL, level);
7148   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7149   {
7150     putFileChunkBE(file, "ELEM", chunk_size);
7151     SaveLevel_ELEM(file, level);
7152   }
7153
7154   for (i = 0; i < NUM_ENVELOPES; i++)
7155   {
7156     int element = EL_ENVELOPE_1 + i;
7157
7158     chunk_size = SaveLevel_NOTE(NULL, level, element);
7159     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7160     {
7161       putFileChunkBE(file, "NOTE", chunk_size);
7162       SaveLevel_NOTE(file, level, element);
7163     }
7164   }
7165
7166   /* if not using template level, check for non-default custom/group elements */
7167   if (!level->use_custom_template || save_as_template)
7168   {
7169     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7170     {
7171       int element = EL_CUSTOM_START + i;
7172
7173       chunk_size = SaveLevel_CUSX(NULL, level, element);
7174       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7175       {
7176         putFileChunkBE(file, "CUSX", chunk_size);
7177         SaveLevel_CUSX(file, level, element);
7178       }
7179     }
7180
7181     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7182     {
7183       int element = EL_GROUP_START + i;
7184
7185       chunk_size = SaveLevel_GRPX(NULL, level, element);
7186       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7187       {
7188         putFileChunkBE(file, "GRPX", chunk_size);
7189         SaveLevel_GRPX(file, level, element);
7190       }
7191     }
7192   }
7193
7194   fclose(file);
7195
7196   SetFilePermissions(filename, PERMS_PRIVATE);
7197 }
7198
7199 void SaveLevel(int nr)
7200 {
7201   char *filename = getDefaultLevelFilename(nr);
7202
7203   SaveLevelFromFilename(&level, filename, FALSE);
7204 }
7205
7206 void SaveLevelTemplate()
7207 {
7208   char *filename = getLocalLevelTemplateFilename();
7209
7210   SaveLevelFromFilename(&level, filename, TRUE);
7211 }
7212
7213 boolean SaveLevelChecked(int nr)
7214 {
7215   char *filename = getDefaultLevelFilename(nr);
7216   boolean new_level = !fileExists(filename);
7217   boolean level_saved = FALSE;
7218
7219   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7220   {
7221     SaveLevel(nr);
7222
7223     if (new_level)
7224       Request("Level saved!", REQ_CONFIRM);
7225
7226     level_saved = TRUE;
7227   }
7228
7229   return level_saved;
7230 }
7231
7232 void DumpLevel(struct LevelInfo *level)
7233 {
7234   if (level->no_level_file || level->no_valid_file)
7235   {
7236     Error(ERR_WARN, "cannot dump -- no valid level file found");
7237
7238     return;
7239   }
7240
7241   PrintLine("-", 79);
7242   Print("Level xxx (file version %08d, game version %08d)\n",
7243         level->file_version, level->game_version);
7244   PrintLine("-", 79);
7245
7246   Print("Level author: '%s'\n", level->author);
7247   Print("Level title:  '%s'\n", level->name);
7248   Print("\n");
7249   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7250   Print("\n");
7251   Print("Level time:  %d seconds\n", level->time);
7252   Print("Gems needed: %d\n", level->gems_needed);
7253   Print("\n");
7254   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7255   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7256   Print("Time for light:      %d seconds\n", level->time_light);
7257   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7258   Print("\n");
7259   Print("Amoeba speed: %d\n", level->amoeba_speed);
7260   Print("\n");
7261
7262   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7263   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7264   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7265   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7266   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7267
7268   PrintLine("-", 79);
7269 }
7270
7271
7272 /* ========================================================================= */
7273 /* tape file functions                                                       */
7274 /* ========================================================================= */
7275
7276 static void setTapeInfoToDefaults()
7277 {
7278   int i;
7279
7280   /* always start with reliable default values (empty tape) */
7281   TapeErase();
7282
7283   /* default values (also for pre-1.2 tapes) with only the first player */
7284   tape.player_participates[0] = TRUE;
7285   for (i = 1; i < MAX_PLAYERS; i++)
7286     tape.player_participates[i] = FALSE;
7287
7288   /* at least one (default: the first) player participates in every tape */
7289   tape.num_participating_players = 1;
7290
7291   tape.level_nr = level_nr;
7292   tape.counter = 0;
7293   tape.changed = FALSE;
7294
7295   tape.recording = FALSE;
7296   tape.playing = FALSE;
7297   tape.pausing = FALSE;
7298
7299   tape.no_valid_file = FALSE;
7300 }
7301
7302 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7303 {
7304   tape->file_version = getFileVersion(file);
7305   tape->game_version = getFileVersion(file);
7306
7307   return chunk_size;
7308 }
7309
7310 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7311 {
7312   int i;
7313
7314   tape->random_seed = getFile32BitBE(file);
7315   tape->date        = getFile32BitBE(file);
7316   tape->length      = getFile32BitBE(file);
7317
7318   /* read header fields that are new since version 1.2 */
7319   if (tape->file_version >= FILE_VERSION_1_2)
7320   {
7321     byte store_participating_players = getFile8Bit(file);
7322     int engine_version;
7323
7324     /* since version 1.2, tapes store which players participate in the tape */
7325     tape->num_participating_players = 0;
7326     for (i = 0; i < MAX_PLAYERS; i++)
7327     {
7328       tape->player_participates[i] = FALSE;
7329
7330       if (store_participating_players & (1 << i))
7331       {
7332         tape->player_participates[i] = TRUE;
7333         tape->num_participating_players++;
7334       }
7335     }
7336
7337     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7338
7339     engine_version = getFileVersion(file);
7340     if (engine_version > 0)
7341       tape->engine_version = engine_version;
7342     else
7343       tape->engine_version = tape->game_version;
7344   }
7345
7346   return chunk_size;
7347 }
7348
7349 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7350 {
7351   int level_identifier_size;
7352   int i;
7353
7354   level_identifier_size = getFile16BitBE(file);
7355
7356   tape->level_identifier =
7357     checked_realloc(tape->level_identifier, level_identifier_size);
7358
7359   for (i = 0; i < level_identifier_size; i++)
7360     tape->level_identifier[i] = getFile8Bit(file);
7361
7362   tape->level_nr = getFile16BitBE(file);
7363
7364   chunk_size = 2 + level_identifier_size + 2;
7365
7366   return chunk_size;
7367 }
7368
7369 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7370 {
7371   int i, j;
7372   int chunk_size_expected =
7373     (tape->num_participating_players + 1) * tape->length;
7374
7375   if (chunk_size_expected != chunk_size)
7376   {
7377     ReadUnusedBytesFromFile(file, chunk_size);
7378     return chunk_size_expected;
7379   }
7380
7381   for (i = 0; i < tape->length; i++)
7382   {
7383     if (i >= MAX_TAPE_LEN)
7384     {
7385       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7386             MAX_TAPE_LEN);
7387
7388       // tape too large; read and ignore remaining tape data from this chunk
7389       for (;i < tape->length; i++)
7390         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7391
7392       break;
7393     }
7394
7395     for (j = 0; j < MAX_PLAYERS; j++)
7396     {
7397       tape->pos[i].action[j] = MV_NONE;
7398
7399       if (tape->player_participates[j])
7400         tape->pos[i].action[j] = getFile8Bit(file);
7401     }
7402
7403     tape->pos[i].delay = getFile8Bit(file);
7404
7405     if (tape->file_version == FILE_VERSION_1_0)
7406     {
7407       /* eliminate possible diagonal moves in old tapes */
7408       /* this is only for backward compatibility */
7409
7410       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7411       byte action = tape->pos[i].action[0];
7412       int k, num_moves = 0;
7413
7414       for (k = 0; k<4; k++)
7415       {
7416         if (action & joy_dir[k])
7417         {
7418           tape->pos[i + num_moves].action[0] = joy_dir[k];
7419           if (num_moves > 0)
7420             tape->pos[i + num_moves].delay = 0;
7421           num_moves++;
7422         }
7423       }
7424
7425       if (num_moves > 1)
7426       {
7427         num_moves--;
7428         i += num_moves;
7429         tape->length += num_moves;
7430       }
7431     }
7432     else if (tape->file_version < FILE_VERSION_2_0)
7433     {
7434       /* convert pre-2.0 tapes to new tape format */
7435
7436       if (tape->pos[i].delay > 1)
7437       {
7438         /* action part */
7439         tape->pos[i + 1] = tape->pos[i];
7440         tape->pos[i + 1].delay = 1;
7441
7442         /* delay part */
7443         for (j = 0; j < MAX_PLAYERS; j++)
7444           tape->pos[i].action[j] = MV_NONE;
7445         tape->pos[i].delay--;
7446
7447         i++;
7448         tape->length++;
7449       }
7450     }
7451
7452     if (checkEndOfFile(file))
7453       break;
7454   }
7455
7456   if (i != tape->length)
7457     chunk_size = (tape->num_participating_players + 1) * i;
7458
7459   return chunk_size;
7460 }
7461
7462 void LoadTape_SokobanSolution(char *filename)
7463 {
7464   File *file;
7465   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7466
7467   if (!(file = openFile(filename, MODE_READ)))
7468   {
7469     tape.no_valid_file = TRUE;
7470
7471     return;
7472   }
7473
7474   while (!checkEndOfFile(file))
7475   {
7476     unsigned char c = getByteFromFile(file);
7477
7478     if (checkEndOfFile(file))
7479       break;
7480
7481     switch (c)
7482     {
7483       case 'u':
7484       case 'U':
7485         tape.pos[tape.length].action[0] = MV_UP;
7486         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7487         tape.length++;
7488         break;
7489
7490       case 'd':
7491       case 'D':
7492         tape.pos[tape.length].action[0] = MV_DOWN;
7493         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7494         tape.length++;
7495         break;
7496
7497       case 'l':
7498       case 'L':
7499         tape.pos[tape.length].action[0] = MV_LEFT;
7500         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7501         tape.length++;
7502         break;
7503
7504       case 'r':
7505       case 'R':
7506         tape.pos[tape.length].action[0] = MV_RIGHT;
7507         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7508         tape.length++;
7509         break;
7510
7511       case '\n':
7512       case '\r':
7513       case '\t':
7514       case ' ':
7515         /* ignore white-space characters */
7516         break;
7517
7518       default:
7519         tape.no_valid_file = TRUE;
7520
7521         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7522
7523         break;
7524     }
7525   }
7526
7527   closeFile(file);
7528
7529   if (tape.no_valid_file)
7530     return;
7531
7532   tape.length_frames  = GetTapeLengthFrames();
7533   tape.length_seconds = GetTapeLengthSeconds();
7534 }
7535
7536 void LoadTapeFromFilename(char *filename)
7537 {
7538   char cookie[MAX_LINE_LEN];
7539   char chunk_name[CHUNK_ID_LEN + 1];
7540   File *file;
7541   int chunk_size;
7542
7543   /* always start with reliable default values */
7544   setTapeInfoToDefaults();
7545
7546   if (strSuffix(filename, ".sln"))
7547   {
7548     LoadTape_SokobanSolution(filename);
7549
7550     return;
7551   }
7552
7553   if (!(file = openFile(filename, MODE_READ)))
7554   {
7555     tape.no_valid_file = TRUE;
7556
7557     return;
7558   }
7559
7560   getFileChunkBE(file, chunk_name, NULL);
7561   if (strEqual(chunk_name, "RND1"))
7562   {
7563     getFile32BitBE(file);               /* not used */
7564
7565     getFileChunkBE(file, chunk_name, NULL);
7566     if (!strEqual(chunk_name, "TAPE"))
7567     {
7568       tape.no_valid_file = TRUE;
7569
7570       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7571
7572       closeFile(file);
7573
7574       return;
7575     }
7576   }
7577   else  /* check for pre-2.0 file format with cookie string */
7578   {
7579     strcpy(cookie, chunk_name);
7580     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7581       cookie[4] = '\0';
7582     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7583       cookie[strlen(cookie) - 1] = '\0';
7584
7585     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7586     {
7587       tape.no_valid_file = TRUE;
7588
7589       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7590
7591       closeFile(file);
7592
7593       return;
7594     }
7595
7596     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7597     {
7598       tape.no_valid_file = TRUE;
7599
7600       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7601
7602       closeFile(file);
7603
7604       return;
7605     }
7606
7607     /* pre-2.0 tape files have no game version, so use file version here */
7608     tape.game_version = tape.file_version;
7609   }
7610
7611   if (tape.file_version < FILE_VERSION_1_2)
7612   {
7613     /* tape files from versions before 1.2.0 without chunk structure */
7614     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7615     LoadTape_BODY(file, 2 * tape.length,      &tape);
7616   }
7617   else
7618   {
7619     static struct
7620     {
7621       char *name;
7622       int size;
7623       int (*loader)(File *, int, struct TapeInfo *);
7624     }
7625     chunk_info[] =
7626     {
7627       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7628       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7629       { "INFO", -1,                     LoadTape_INFO },
7630       { "BODY", -1,                     LoadTape_BODY },
7631       {  NULL,  0,                      NULL }
7632     };
7633
7634     while (getFileChunkBE(file, chunk_name, &chunk_size))
7635     {
7636       int i = 0;
7637
7638       while (chunk_info[i].name != NULL &&
7639              !strEqual(chunk_name, chunk_info[i].name))
7640         i++;
7641
7642       if (chunk_info[i].name == NULL)
7643       {
7644         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7645               chunk_name, filename);
7646         ReadUnusedBytesFromFile(file, chunk_size);
7647       }
7648       else if (chunk_info[i].size != -1 &&
7649                chunk_info[i].size != chunk_size)
7650       {
7651         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7652               chunk_size, chunk_name, filename);
7653         ReadUnusedBytesFromFile(file, chunk_size);
7654       }
7655       else
7656       {
7657         /* call function to load this tape chunk */
7658         int chunk_size_expected =
7659           (chunk_info[i].loader)(file, chunk_size, &tape);
7660
7661         /* the size of some chunks cannot be checked before reading other
7662            chunks first (like "HEAD" and "BODY") that contain some header
7663            information, so check them here */
7664         if (chunk_size_expected != chunk_size)
7665         {
7666           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7667                 chunk_size, chunk_name, filename);
7668         }
7669       }
7670     }
7671   }
7672
7673   closeFile(file);
7674
7675   tape.length_frames  = GetTapeLengthFrames();
7676   tape.length_seconds = GetTapeLengthSeconds();
7677
7678 #if 0
7679   printf("::: tape file version: %d\n",   tape.file_version);
7680   printf("::: tape game version: %d\n",   tape.game_version);
7681   printf("::: tape engine version: %d\n", tape.engine_version);
7682 #endif
7683 }
7684
7685 void LoadTape(int nr)
7686 {
7687   char *filename = getTapeFilename(nr);
7688
7689   LoadTapeFromFilename(filename);
7690 }
7691
7692 void LoadSolutionTape(int nr)
7693 {
7694   char *filename = getSolutionTapeFilename(nr);
7695
7696   LoadTapeFromFilename(filename);
7697
7698   if (TAPE_IS_EMPTY(tape) &&
7699       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7700       level.native_sp_level->demo.is_available)
7701     CopyNativeTape_SP_to_RND(&level);
7702 }
7703
7704 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7705 {
7706   putFileVersion(file, tape->file_version);
7707   putFileVersion(file, tape->game_version);
7708 }
7709
7710 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7711 {
7712   int i;
7713   byte store_participating_players = 0;
7714
7715   /* set bits for participating players for compact storage */
7716   for (i = 0; i < MAX_PLAYERS; i++)
7717     if (tape->player_participates[i])
7718       store_participating_players |= (1 << i);
7719
7720   putFile32BitBE(file, tape->random_seed);
7721   putFile32BitBE(file, tape->date);
7722   putFile32BitBE(file, tape->length);
7723
7724   putFile8Bit(file, store_participating_players);
7725
7726   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7727   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7728
7729   putFileVersion(file, tape->engine_version);
7730 }
7731
7732 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7733 {
7734   int level_identifier_size = strlen(tape->level_identifier) + 1;
7735   int i;
7736
7737   putFile16BitBE(file, level_identifier_size);
7738
7739   for (i = 0; i < level_identifier_size; i++)
7740     putFile8Bit(file, tape->level_identifier[i]);
7741
7742   putFile16BitBE(file, tape->level_nr);
7743 }
7744
7745 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7746 {
7747   int i, j;
7748
7749   for (i = 0; i < tape->length; i++)
7750   {
7751     for (j = 0; j < MAX_PLAYERS; j++)
7752       if (tape->player_participates[j])
7753         putFile8Bit(file, tape->pos[i].action[j]);
7754
7755     putFile8Bit(file, tape->pos[i].delay);
7756   }
7757 }
7758
7759 void SaveTape(int nr)
7760 {
7761   char *filename = getTapeFilename(nr);
7762   FILE *file;
7763   int num_participating_players = 0;
7764   int info_chunk_size;
7765   int body_chunk_size;
7766   int i;
7767
7768   InitTapeDirectory(leveldir_current->subdir);
7769
7770   if (!(file = fopen(filename, MODE_WRITE)))
7771   {
7772     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7773     return;
7774   }
7775
7776   tape.file_version = FILE_VERSION_ACTUAL;
7777   tape.game_version = GAME_VERSION_ACTUAL;
7778
7779   /* count number of participating players  */
7780   for (i = 0; i < MAX_PLAYERS; i++)
7781     if (tape.player_participates[i])
7782       num_participating_players++;
7783
7784   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7785   body_chunk_size = (num_participating_players + 1) * tape.length;
7786
7787   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7788   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7789
7790   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7791   SaveTape_VERS(file, &tape);
7792
7793   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7794   SaveTape_HEAD(file, &tape);
7795
7796   putFileChunkBE(file, "INFO", info_chunk_size);
7797   SaveTape_INFO(file, &tape);
7798
7799   putFileChunkBE(file, "BODY", body_chunk_size);
7800   SaveTape_BODY(file, &tape);
7801
7802   fclose(file);
7803
7804   SetFilePermissions(filename, PERMS_PRIVATE);
7805
7806   tape.changed = FALSE;
7807 }
7808
7809 boolean SaveTapeChecked(int nr)
7810 {
7811   char *filename = getTapeFilename(nr);
7812   boolean new_tape = !fileExists(filename);
7813   boolean tape_saved = FALSE;
7814
7815   if (new_tape || Request("Replace old tape?", REQ_ASK))
7816   {
7817     SaveTape(nr);
7818
7819     if (new_tape)
7820       Request("Tape saved!", REQ_CONFIRM);
7821
7822     tape_saved = TRUE;
7823   }
7824
7825   return tape_saved;
7826 }
7827
7828 void DumpTape(struct TapeInfo *tape)
7829 {
7830   int tape_frame_counter;
7831   int i, j;
7832
7833   if (tape->no_valid_file)
7834   {
7835     Error(ERR_WARN, "cannot dump -- no valid tape file found");
7836
7837     return;
7838   }
7839
7840   PrintLine("-", 79);
7841   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
7842         tape->level_nr, tape->file_version, tape->game_version);
7843   Print("                  (effective engine version %08d)\n",
7844         tape->engine_version);
7845   Print("Level series identifier: '%s'\n", tape->level_identifier);
7846   PrintLine("-", 79);
7847
7848   tape_frame_counter = 0;
7849
7850   for (i = 0; i < tape->length; i++)
7851   {
7852     if (i >= MAX_TAPE_LEN)
7853       break;
7854
7855     Print("%04d: ", i);
7856
7857     for (j = 0; j < MAX_PLAYERS; j++)
7858     {
7859       if (tape->player_participates[j])
7860       {
7861         int action = tape->pos[i].action[j];
7862
7863         Print("%d:%02x ", j, action);
7864         Print("[%c%c%c%c|%c%c] - ",
7865               (action & JOY_LEFT ? '<' : ' '),
7866               (action & JOY_RIGHT ? '>' : ' '),
7867               (action & JOY_UP ? '^' : ' '),
7868               (action & JOY_DOWN ? 'v' : ' '),
7869               (action & JOY_BUTTON_1 ? '1' : ' '),
7870               (action & JOY_BUTTON_2 ? '2' : ' '));
7871       }
7872     }
7873
7874     Print("(%03d) ", tape->pos[i].delay);
7875     Print("[%05d]\n", tape_frame_counter);
7876
7877     tape_frame_counter += tape->pos[i].delay;
7878   }
7879
7880   PrintLine("-", 79);
7881 }
7882
7883
7884 /* ========================================================================= */
7885 /* score file functions                                                      */
7886 /* ========================================================================= */
7887
7888 void LoadScore(int nr)
7889 {
7890   int i;
7891   char *filename = getScoreFilename(nr);
7892   char cookie[MAX_LINE_LEN];
7893   char line[MAX_LINE_LEN];
7894   char *line_ptr;
7895   FILE *file;
7896
7897   /* always start with reliable default values */
7898   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7899   {
7900     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
7901     highscore[i].Score = 0;
7902   }
7903
7904   if (!(file = fopen(filename, MODE_READ)))
7905     return;
7906
7907   /* check file identifier */
7908   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
7909     cookie[0] = '\0';
7910   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7911     cookie[strlen(cookie) - 1] = '\0';
7912
7913   if (!checkCookieString(cookie, SCORE_COOKIE))
7914   {
7915     Error(ERR_WARN, "unknown format of score file '%s'", filename);
7916     fclose(file);
7917     return;
7918   }
7919
7920   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7921   {
7922     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
7923       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
7924     if (fgets(line, MAX_LINE_LEN, file) == NULL)
7925       line[0] = '\0';
7926
7927     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
7928       line[strlen(line) - 1] = '\0';
7929
7930     for (line_ptr = line; *line_ptr; line_ptr++)
7931     {
7932       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
7933       {
7934         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
7935         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
7936         break;
7937       }
7938     }
7939   }
7940
7941   fclose(file);
7942 }
7943
7944 void SaveScore(int nr)
7945 {
7946   int i;
7947   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
7948   char *filename = getScoreFilename(nr);
7949   FILE *file;
7950
7951   InitScoreDirectory(leveldir_current->subdir);
7952
7953   if (!(file = fopen(filename, MODE_WRITE)))
7954   {
7955     Error(ERR_WARN, "cannot save score for level %d", nr);
7956     return;
7957   }
7958
7959   fprintf(file, "%s\n\n", SCORE_COOKIE);
7960
7961   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
7962     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
7963
7964   fclose(file);
7965
7966   SetFilePermissions(filename, permissions);
7967 }
7968
7969
7970 /* ========================================================================= */
7971 /* setup file functions                                                      */
7972 /* ========================================================================= */
7973
7974 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
7975
7976 /* global setup */
7977 #define SETUP_TOKEN_PLAYER_NAME                 0
7978 #define SETUP_TOKEN_SOUND                       1
7979 #define SETUP_TOKEN_SOUND_LOOPS                 2
7980 #define SETUP_TOKEN_SOUND_MUSIC                 3
7981 #define SETUP_TOKEN_SOUND_SIMPLE                4
7982 #define SETUP_TOKEN_TOONS                       5
7983 #define SETUP_TOKEN_SCROLL_DELAY                6
7984 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
7985 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
7986 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
7987 #define SETUP_TOKEN_FADE_SCREENS                10
7988 #define SETUP_TOKEN_AUTORECORD                  11
7989 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
7990 #define SETUP_TOKEN_QUICK_DOORS                 13
7991 #define SETUP_TOKEN_TEAM_MODE                   14
7992 #define SETUP_TOKEN_HANDICAP                    15
7993 #define SETUP_TOKEN_SKIP_LEVELS                 16
7994 #define SETUP_TOKEN_INCREMENT_LEVELS            17
7995 #define SETUP_TOKEN_TIME_LIMIT                  18
7996 #define SETUP_TOKEN_FULLSCREEN                  19
7997 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
7998 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
7999 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8000 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8001 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8002 #define SETUP_TOKEN_QUICK_SWITCH                25
8003 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8004 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8005 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8006 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8007 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8008 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8009 #define SETUP_TOKEN_GRAPHICS_SET                32
8010 #define SETUP_TOKEN_SOUNDS_SET                  33
8011 #define SETUP_TOKEN_MUSIC_SET                   34
8012 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8013 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8014 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8015 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8016 #define SETUP_TOKEN_VOLUME_LOOPS                39
8017 #define SETUP_TOKEN_VOLUME_MUSIC                40
8018 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8019 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8020 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8021
8022 #define NUM_GLOBAL_SETUP_TOKENS                 44
8023
8024 /* editor setup */
8025 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8026 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8027 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8028 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8029 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8030 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8031
8032 #define NUM_EDITOR_SETUP_TOKENS                 6
8033
8034 /* editor cascade setup */
8035 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8036 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8037 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8038 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8039 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8040 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8041 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8042 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8043 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8044 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8045 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8046 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8047 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8048 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8049 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8050
8051 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8052
8053 /* shortcut setup */
8054 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8055 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8056 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8057 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8058 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8059 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8060 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8061 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8062 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8063 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8064 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8065 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8066 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8067 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8068 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8069 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8070 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8071 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8072 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8073 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8074 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8075
8076 #define NUM_SHORTCUT_SETUP_TOKENS               21
8077
8078 /* player setup */
8079 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8080 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8081 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8082 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8083 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8084 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8085 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8086 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8087 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8088 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8089 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8090 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8091 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8092 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8093 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8094 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8095
8096 #define NUM_PLAYER_SETUP_TOKENS                 16
8097
8098 /* system setup */
8099 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8100 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8101 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8102
8103 #define NUM_SYSTEM_SETUP_TOKENS                 3
8104
8105 /* internal setup */
8106 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8107 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8108 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8109 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8110 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8111 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8112 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8113 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8114 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8115 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8116 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8117 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8118 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8119 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8120 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8121 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8122 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8123 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8124 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8125
8126 #define NUM_INTERNAL_SETUP_TOKENS               19
8127
8128 /* debug setup */
8129 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8130 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8131 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8132 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8133 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8134 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8135 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8136 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8137 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8138 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8139 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8140 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8141 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8142 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8143 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8144 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8145 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8146 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8147 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8148 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8149 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8150 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8151 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8152
8153 #define NUM_DEBUG_SETUP_TOKENS                  23
8154
8155 /* options setup */
8156 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8157
8158 #define NUM_OPTIONS_SETUP_TOKENS                1
8159
8160
8161 static struct SetupInfo si;
8162 static struct SetupEditorInfo sei;
8163 static struct SetupEditorCascadeInfo seci;
8164 static struct SetupShortcutInfo ssi;
8165 static struct SetupInputInfo sii;
8166 static struct SetupSystemInfo syi;
8167 static struct SetupInternalInfo sxi;
8168 static struct SetupDebugInfo sdi;
8169 static struct OptionInfo soi;
8170
8171 static struct TokenInfo global_setup_tokens[] =
8172 {
8173   { TYPE_STRING, &si.player_name,             "player_name"             },
8174   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8175   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8176   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8177   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8178   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8179   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8180   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8181   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8182   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8183   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8184   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8185   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8186   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8187   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8188   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8189   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8190   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8191   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8192   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8193   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8194   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8195   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8196   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8197   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8198   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8199   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8200   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8201   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8202   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8203   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8204   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8205   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8206   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8207   { TYPE_STRING, &si.music_set,               "music_set"               },
8208   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8209   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8210   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8211   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8212   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8213   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8214   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8215   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8216   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8217 };
8218
8219 static struct TokenInfo editor_setup_tokens[] =
8220 {
8221   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8222   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8223   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8224   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8225   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8226   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8227 };
8228
8229 static struct TokenInfo editor_cascade_setup_tokens[] =
8230 {
8231   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8232   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8233   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8234   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8235   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8236   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8237   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8238   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8239   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8240   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8241   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8242   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8243   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8244   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8245   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8246 };
8247
8248 static struct TokenInfo shortcut_setup_tokens[] =
8249 {
8250   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8251   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8252   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8253   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8254   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8255   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8256   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8257   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8258   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8259   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8260   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8261   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8262   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8263   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8264   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8265   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8266   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8267   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8268   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8269   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8270   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8271 };
8272
8273 static struct TokenInfo player_setup_tokens[] =
8274 {
8275   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8276   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8277   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8278   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8279   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8280   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8281   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8282   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8283   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8284   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8285   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8286   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8287   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8288   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8289   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8290   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8291 };
8292
8293 static struct TokenInfo system_setup_tokens[] =
8294 {
8295   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8296   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8297   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8298 };
8299
8300 static struct TokenInfo internal_setup_tokens[] =
8301 {
8302   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8303   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8304   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8305   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8306   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8307   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8308   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8309   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8310   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8311   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8312   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8313   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8314   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8315   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8316   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8317   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8318   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8319   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8320   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8321 };
8322
8323 static struct TokenInfo debug_setup_tokens[] =
8324 {
8325   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8326   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8327   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8328   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8329   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8330   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8331   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8332   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8333   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8334   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8335   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8336   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8337   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8338   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8339   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8340   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8341   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8342   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8343   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8344   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8345   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8346   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8347   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8348 };
8349
8350 static struct TokenInfo options_setup_tokens[] =
8351 {
8352   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8353 };
8354
8355 static char *get_corrected_login_name(char *login_name)
8356 {
8357   /* needed because player name must be a fixed length string */
8358   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8359
8360   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8361   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8362
8363   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8364     if (strchr(login_name_new, ' '))
8365       *strchr(login_name_new, ' ') = '\0';
8366
8367   return login_name_new;
8368 }
8369
8370 static void setSetupInfoToDefaults(struct SetupInfo *si)
8371 {
8372   int i;
8373
8374   si->player_name = get_corrected_login_name(getLoginName());
8375
8376   si->sound = TRUE;
8377   si->sound_loops = TRUE;
8378   si->sound_music = TRUE;
8379   si->sound_simple = TRUE;
8380   si->toons = TRUE;
8381   si->scroll_delay = TRUE;
8382   si->scroll_delay_value = STD_SCROLL_DELAY;
8383   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8384   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8385   si->fade_screens = TRUE;
8386   si->autorecord = TRUE;
8387   si->show_titlescreen = TRUE;
8388   si->quick_doors = FALSE;
8389   si->team_mode = FALSE;
8390   si->handicap = TRUE;
8391   si->skip_levels = TRUE;
8392   si->increment_levels = TRUE;
8393   si->time_limit = TRUE;
8394   si->fullscreen = FALSE;
8395   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8396   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8397   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8398   si->ask_on_escape = TRUE;
8399   si->ask_on_escape_editor = TRUE;
8400   si->quick_switch = FALSE;
8401   si->input_on_focus = FALSE;
8402   si->prefer_aga_graphics = TRUE;
8403   si->game_frame_delay = GAME_FRAME_DELAY;
8404   si->sp_show_border_elements = FALSE;
8405   si->small_game_graphics = FALSE;
8406   si->show_snapshot_buttons = FALSE;
8407
8408   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8409   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8410   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8411
8412   si->override_level_graphics = FALSE;
8413   si->override_level_sounds = FALSE;
8414   si->override_level_music = FALSE;
8415
8416   si->volume_simple = 100;              /* percent */
8417   si->volume_loops = 100;               /* percent */
8418   si->volume_music = 100;               /* percent */
8419
8420   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8421   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8422   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8423
8424   si->editor.el_boulderdash             = TRUE;
8425   si->editor.el_emerald_mine            = TRUE;
8426   si->editor.el_emerald_mine_club       = TRUE;
8427   si->editor.el_more                    = TRUE;
8428   si->editor.el_sokoban                 = TRUE;
8429   si->editor.el_supaplex                = TRUE;
8430   si->editor.el_diamond_caves           = TRUE;
8431   si->editor.el_dx_boulderdash          = TRUE;
8432   si->editor.el_chars                   = TRUE;
8433   si->editor.el_steel_chars             = TRUE;
8434
8435   si->editor.el_classic                 = TRUE;
8436   si->editor.el_custom                  = TRUE;
8437
8438   si->editor.el_user_defined            = FALSE;
8439   si->editor.el_dynamic                 = TRUE;
8440
8441   si->editor.el_headlines               = TRUE;
8442
8443   si->editor.show_element_token         = FALSE;
8444
8445   si->editor.use_template_for_new_levels = TRUE;
8446
8447   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8448   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8449   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8450
8451   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8452   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8453   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8454   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8455   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8456
8457   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8458   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8459   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8460   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8461   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8462   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8463
8464   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8465   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8466   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8467
8468   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8469   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8470   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8471   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8472
8473   for (i = 0; i < MAX_PLAYERS; i++)
8474   {
8475     si->input[i].use_joystick = FALSE;
8476     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8477     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8478     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8479     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8480     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8481     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8482     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8483     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8484     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8485     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8486     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8487     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8488     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8489     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8490     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8491   }
8492
8493   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8494   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8495   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8496
8497   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8498   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8499   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8500   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8501   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8502   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8503   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8504
8505   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8506
8507   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8508   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8509   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8510
8511   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8512   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8513   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8514
8515   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8516   si->internal.choose_from_top_leveldir = FALSE;
8517   si->internal.show_scaling_in_title = TRUE;
8518
8519   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8520   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8521
8522   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8523   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8524   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8525   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8526   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8527   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8528   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8529   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8530   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8531   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8532
8533   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8534   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8535   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8536   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8537   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8538   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8539   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8540   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8541   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8542   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8543
8544   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8545   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8546
8547   si->debug.show_frames_per_second = FALSE;
8548
8549   si->options.verbose = FALSE;
8550
8551 #if defined(PLATFORM_ANDROID)
8552   si->fullscreen = TRUE;
8553 #endif
8554 }
8555
8556 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8557 {
8558   si->editor_cascade.el_bd              = TRUE;
8559   si->editor_cascade.el_em              = TRUE;
8560   si->editor_cascade.el_emc             = TRUE;
8561   si->editor_cascade.el_rnd             = TRUE;
8562   si->editor_cascade.el_sb              = TRUE;
8563   si->editor_cascade.el_sp              = TRUE;
8564   si->editor_cascade.el_dc              = TRUE;
8565   si->editor_cascade.el_dx              = TRUE;
8566
8567   si->editor_cascade.el_chars           = FALSE;
8568   si->editor_cascade.el_steel_chars     = FALSE;
8569   si->editor_cascade.el_ce              = FALSE;
8570   si->editor_cascade.el_ge              = FALSE;
8571   si->editor_cascade.el_ref             = FALSE;
8572   si->editor_cascade.el_user            = FALSE;
8573   si->editor_cascade.el_dynamic         = FALSE;
8574 }
8575
8576 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8577
8578 static char *getHideSetupToken(void *setup_value)
8579 {
8580   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8581
8582   if (setup_value != NULL)
8583     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8584
8585   return hide_setup_token;
8586 }
8587
8588 static void setHideSetupEntry(void *setup_value_raw)
8589 {
8590   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8591   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8592
8593   char *hide_setup_token = getHideSetupToken(setup_value);
8594
8595   if (setup_value != NULL)
8596     setHashEntry(hide_setup_hash, hide_setup_token, "");
8597 }
8598
8599 boolean hideSetupEntry(void *setup_value)
8600 {
8601   char *hide_setup_token = getHideSetupToken(setup_value);
8602
8603   return (setup_value != NULL &&
8604           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8605 }
8606
8607 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8608                                       struct TokenInfo *token_info,
8609                                       int token_nr, char *token_text)
8610 {
8611   char *token_hide_text = getStringCat2(token_text, ".hide");
8612   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8613
8614   /* set the value of this setup option in the setup option structure */
8615   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8616
8617   /* check if this setup option should be hidden in the setup menu */
8618   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8619     setHideSetupEntry(token_info[token_nr].value);
8620 }
8621
8622 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8623                                       struct TokenInfo *token_info,
8624                                       int token_nr)
8625 {
8626   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8627                             token_info[token_nr].text);
8628 }
8629
8630 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8631 {
8632   int i, pnr;
8633
8634   if (!setup_file_hash)
8635     return;
8636
8637   if (hide_setup_hash == NULL)
8638     hide_setup_hash = newSetupFileHash();
8639
8640   /* global setup */
8641   si = setup;
8642   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8643     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8644   setup = si;
8645
8646   /* editor setup */
8647   sei = setup.editor;
8648   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8649     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8650   setup.editor = sei;
8651
8652   /* shortcut setup */
8653   ssi = setup.shortcut;
8654   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8655     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8656   setup.shortcut = ssi;
8657
8658   /* player setup */
8659   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8660   {
8661     char prefix[30];
8662
8663     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8664
8665     sii = setup.input[pnr];
8666     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8667     {
8668       char full_token[100];
8669
8670       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8671       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8672                                 full_token);
8673     }
8674     setup.input[pnr] = sii;
8675   }
8676
8677   /* system setup */
8678   syi = setup.system;
8679   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8680     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8681   setup.system = syi;
8682
8683   /* internal setup */
8684   sxi = setup.internal;
8685   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8686     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8687   setup.internal = sxi;
8688
8689   /* debug setup */
8690   sdi = setup.debug;
8691   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8692     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8693   setup.debug = sdi;
8694
8695   /* options setup */
8696   soi = setup.options;
8697   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8698     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8699   setup.options = soi;
8700 }
8701
8702 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8703 {
8704   int i;
8705
8706   if (!setup_file_hash)
8707     return;
8708
8709   /* editor cascade setup */
8710   seci = setup.editor_cascade;
8711   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8712     setSetupInfo(editor_cascade_setup_tokens, i,
8713                  getHashEntry(setup_file_hash,
8714                               editor_cascade_setup_tokens[i].text));
8715   setup.editor_cascade = seci;
8716 }
8717
8718 void LoadSetupFromFilename(char *filename)
8719 {
8720   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8721
8722   if (setup_file_hash)
8723   {
8724     decodeSetupFileHash(setup_file_hash);
8725
8726     freeSetupFileHash(setup_file_hash);
8727   }
8728   else
8729   {
8730     Error(ERR_DEBUG, "using default setup values");
8731   }
8732 }
8733
8734 static void LoadSetup_SpecialPostProcessing()
8735 {
8736   char *player_name_new;
8737
8738   /* needed to work around problems with fixed length strings */
8739   player_name_new = get_corrected_login_name(setup.player_name);
8740   free(setup.player_name);
8741   setup.player_name = player_name_new;
8742
8743   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8744   if (setup.scroll_delay == FALSE)
8745   {
8746     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8747     setup.scroll_delay = TRUE;                  /* now always "on" */
8748   }
8749
8750   /* make sure that scroll delay value stays inside valid range */
8751   setup.scroll_delay_value =
8752     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8753 }
8754
8755 void LoadSetup()
8756 {
8757   char *filename;
8758
8759   /* always start with reliable default values */
8760   setSetupInfoToDefaults(&setup);
8761
8762   /* try to load setup values from default setup file */
8763   filename = getDefaultSetupFilename();
8764
8765   if (fileExists(filename))
8766     LoadSetupFromFilename(filename);
8767
8768   /* try to load setup values from user setup file */
8769   filename = getSetupFilename();
8770
8771   LoadSetupFromFilename(filename);
8772
8773   LoadSetup_SpecialPostProcessing();
8774 }
8775
8776 void LoadSetup_EditorCascade()
8777 {
8778   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8779   SetupFileHash *setup_file_hash = NULL;
8780
8781   /* always start with reliable default values */
8782   setSetupInfoToDefaults_EditorCascade(&setup);
8783
8784   setup_file_hash = loadSetupFileHash(filename);
8785
8786   if (setup_file_hash)
8787   {
8788     decodeSetupFileHash_EditorCascade(setup_file_hash);
8789
8790     freeSetupFileHash(setup_file_hash);
8791   }
8792
8793   free(filename);
8794 }
8795
8796 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8797                                            char *mapping_line)
8798 {
8799   char mapping_guid[MAX_LINE_LEN];
8800   char *mapping_start, *mapping_end;
8801
8802   // get GUID from game controller mapping line: copy complete line
8803   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8804   mapping_guid[MAX_LINE_LEN - 1] = '\0';
8805
8806   // get GUID from game controller mapping line: cut after GUID part
8807   mapping_start = strchr(mapping_guid, ',');
8808   if (mapping_start != NULL)
8809     *mapping_start = '\0';
8810
8811   // cut newline from game controller mapping line
8812   mapping_end = strchr(mapping_line, '\n');
8813   if (mapping_end != NULL)
8814     *mapping_end = '\0';
8815
8816   // add mapping entry to game controller mappings hash
8817   setHashEntry(mappings_hash, mapping_guid, mapping_line);
8818 }
8819
8820 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
8821                                                  char *filename)
8822 {
8823   FILE *file;
8824
8825   if (!(file = fopen(filename, MODE_READ)))
8826   {
8827     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
8828
8829     return;
8830   }
8831
8832   while (!feof(file))
8833   {
8834     char line[MAX_LINE_LEN];
8835
8836     if (!fgets(line, MAX_LINE_LEN, file))
8837       break;
8838
8839     addGameControllerMappingToHash(mappings_hash, line);
8840   }
8841
8842   fclose(file);
8843 }
8844
8845 void SaveSetup()
8846 {
8847   char *filename = getSetupFilename();
8848   FILE *file;
8849   int i, pnr;
8850
8851   InitUserDataDirectory();
8852
8853   if (!(file = fopen(filename, MODE_WRITE)))
8854   {
8855     Error(ERR_WARN, "cannot write setup file '%s'", filename);
8856     return;
8857   }
8858
8859   fprintFileHeader(file, SETUP_FILENAME);
8860
8861   /* global setup */
8862   si = setup;
8863   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8864   {
8865     /* just to make things nicer :) */
8866     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
8867         i == SETUP_TOKEN_GRAPHICS_SET ||
8868         i == SETUP_TOKEN_VOLUME_SIMPLE ||
8869         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
8870       fprintf(file, "\n");
8871
8872     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
8873   }
8874
8875   /* editor setup */
8876   sei = setup.editor;
8877   fprintf(file, "\n");
8878   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8879     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
8880
8881   /* shortcut setup */
8882   ssi = setup.shortcut;
8883   fprintf(file, "\n");
8884   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8885     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
8886
8887   /* player setup */
8888   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8889   {
8890     char prefix[30];
8891
8892     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8893     fprintf(file, "\n");
8894
8895     sii = setup.input[pnr];
8896     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8897       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
8898   }
8899
8900   /* system setup */
8901   syi = setup.system;
8902   fprintf(file, "\n");
8903   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8904     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
8905
8906   /* internal setup */
8907   /* (internal setup values not saved to user setup file) */
8908
8909   /* debug setup */
8910   sdi = setup.debug;
8911   fprintf(file, "\n");
8912   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8913     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
8914
8915   /* options setup */
8916   soi = setup.options;
8917   fprintf(file, "\n");
8918   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8919     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
8920
8921   fclose(file);
8922
8923   SetFilePermissions(filename, PERMS_PRIVATE);
8924 }
8925
8926 void SaveSetup_EditorCascade()
8927 {
8928   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8929   FILE *file;
8930   int i;
8931
8932   InitUserDataDirectory();
8933
8934   if (!(file = fopen(filename, MODE_WRITE)))
8935   {
8936     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
8937     free(filename);
8938     return;
8939   }
8940
8941   fprintFileHeader(file, EDITORCASCADE_FILENAME);
8942
8943   seci = setup.editor_cascade;
8944   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8945     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
8946
8947   fclose(file);
8948
8949   SetFilePermissions(filename, PERMS_PRIVATE);
8950
8951   free(filename);
8952 }
8953
8954 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
8955                                                   char *filename)
8956 {
8957   FILE *file;
8958
8959   if (!(file = fopen(filename, MODE_WRITE)))
8960   {
8961     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
8962
8963     return;
8964   }
8965
8966   BEGIN_HASH_ITERATION(mappings_hash, itr)
8967   {
8968     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
8969   }
8970   END_HASH_ITERATION(mappings_hash, itr)
8971
8972   fclose(file);
8973 }
8974
8975 void SaveSetup_AddGameControllerMapping(char *mapping)
8976 {
8977   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
8978   SetupFileHash *mappings_hash = newSetupFileHash();
8979
8980   InitUserDataDirectory();
8981
8982   // load existing personal game controller mappings
8983   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
8984
8985   // add new mapping to personal game controller mappings
8986   addGameControllerMappingToHash(mappings_hash, mapping);
8987
8988   // save updated personal game controller mappings
8989   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
8990
8991   freeSetupFileHash(mappings_hash);
8992   free(filename);
8993 }
8994
8995 void LoadCustomElementDescriptions()
8996 {
8997   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
8998   SetupFileHash *setup_file_hash;
8999   int i;
9000
9001   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9002   {
9003     if (element_info[i].custom_description != NULL)
9004     {
9005       free(element_info[i].custom_description);
9006       element_info[i].custom_description = NULL;
9007     }
9008   }
9009
9010   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9011     return;
9012
9013   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9014   {
9015     char *token = getStringCat2(element_info[i].token_name, ".name");
9016     char *value = getHashEntry(setup_file_hash, token);
9017
9018     if (value != NULL)
9019       element_info[i].custom_description = getStringCopy(value);
9020
9021     free(token);
9022   }
9023
9024   freeSetupFileHash(setup_file_hash);
9025 }
9026
9027 static int getElementFromToken(char *token)
9028 {
9029   char *value = getHashEntry(element_token_hash, token);
9030
9031   if (value != NULL)
9032     return atoi(value);
9033
9034   Error(ERR_WARN, "unknown element token '%s'", token);
9035
9036   return EL_UNDEFINED;
9037 }
9038
9039 static int get_token_parameter_value(char *token, char *value_raw)
9040 {
9041   char *suffix;
9042
9043   if (token == NULL || value_raw == NULL)
9044     return ARG_UNDEFINED_VALUE;
9045
9046   suffix = strrchr(token, '.');
9047   if (suffix == NULL)
9048     suffix = token;
9049
9050   if (strEqual(suffix, ".element"))
9051     return getElementFromToken(value_raw);
9052
9053   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9054   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9055 }
9056
9057 void InitMenuDesignSettings_Static()
9058 {
9059   int i;
9060
9061   /* always start with reliable default values from static default config */
9062   for (i = 0; image_config_vars[i].token != NULL; i++)
9063   {
9064     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9065
9066     if (value != NULL)
9067       *image_config_vars[i].value =
9068         get_token_parameter_value(image_config_vars[i].token, value);
9069   }
9070 }
9071
9072 static void InitMenuDesignSettings_SpecialPreProcessing()
9073 {
9074   int i;
9075
9076   /* the following initializes hierarchical values from static configuration */
9077
9078   /* special case: initialize "ARG_DEFAULT" values in static default config */
9079   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9080   titlescreen_initial_first_default.fade_mode  =
9081     title_initial_first_default.fade_mode;
9082   titlescreen_initial_first_default.fade_delay =
9083     title_initial_first_default.fade_delay;
9084   titlescreen_initial_first_default.post_delay =
9085     title_initial_first_default.post_delay;
9086   titlescreen_initial_first_default.auto_delay =
9087     title_initial_first_default.auto_delay;
9088   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9089   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9090   titlescreen_first_default.post_delay = title_first_default.post_delay;
9091   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9092   titlemessage_initial_first_default.fade_mode  =
9093     title_initial_first_default.fade_mode;
9094   titlemessage_initial_first_default.fade_delay =
9095     title_initial_first_default.fade_delay;
9096   titlemessage_initial_first_default.post_delay =
9097     title_initial_first_default.post_delay;
9098   titlemessage_initial_first_default.auto_delay =
9099     title_initial_first_default.auto_delay;
9100   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9101   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9102   titlemessage_first_default.post_delay = title_first_default.post_delay;
9103   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9104
9105   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9106   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9107   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9108   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9109   titlescreen_default.fade_mode  = title_default.fade_mode;
9110   titlescreen_default.fade_delay = title_default.fade_delay;
9111   titlescreen_default.post_delay = title_default.post_delay;
9112   titlescreen_default.auto_delay = title_default.auto_delay;
9113   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9114   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9115   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9116   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9117   titlemessage_default.fade_mode  = title_default.fade_mode;
9118   titlemessage_default.fade_delay = title_default.fade_delay;
9119   titlemessage_default.post_delay = title_default.post_delay;
9120   titlemessage_default.auto_delay = title_default.auto_delay;
9121
9122   /* special case: initialize "ARG_DEFAULT" values in static default config */
9123   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9124   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9125   {
9126     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9127     titlescreen_first[i] = titlescreen_first_default;
9128     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9129     titlemessage_first[i] = titlemessage_first_default;
9130
9131     titlescreen_initial[i] = titlescreen_initial_default;
9132     titlescreen[i] = titlescreen_default;
9133     titlemessage_initial[i] = titlemessage_initial_default;
9134     titlemessage[i] = titlemessage_default;
9135   }
9136
9137   /* special case: initialize "ARG_DEFAULT" values in static default config */
9138   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9139   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9140   {
9141     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9142       continue;
9143
9144     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9145     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9146     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9147   }
9148
9149   /* special case: initialize "ARG_DEFAULT" values in static default config */
9150   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9151   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9152   {
9153     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9154     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9155     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9156
9157     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9158       continue;
9159
9160     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9161   }
9162 }
9163
9164 static void InitMenuDesignSettings_SpecialPostProcessing()
9165 {
9166   static struct
9167   {
9168     struct XY *dst, *src;
9169   }
9170   game_buttons_xy[] =
9171   {
9172     { &game.button.save,        &game.button.stop       },
9173     { &game.button.pause2,      &game.button.pause      },
9174     { &game.button.load,        &game.button.play       },
9175     { &game.button.undo,        &game.button.stop       },
9176     { &game.button.redo,        &game.button.play       },
9177
9178     { NULL,                     NULL                    }
9179   };
9180   int i;
9181
9182   /* special case: initialize later added SETUP list size from LEVELS value */
9183   if (menu.list_size[GAME_MODE_SETUP] == -1)
9184     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9185
9186   /* set default position for snapshot buttons to stop/pause/play buttons */
9187   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9188     if ((*game_buttons_xy[i].dst).x == -1 &&
9189         (*game_buttons_xy[i].dst).y == -1)
9190       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9191 }
9192
9193 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9194 {
9195   static struct
9196   {
9197     struct XYTileSize *dst, *src;
9198     int graphic;
9199   }
9200   editor_buttons_xy[] =
9201   {
9202     {
9203       &editor.button.element_left,      &editor.palette.element_left,
9204       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9205     },
9206     {
9207       &editor.button.element_middle,    &editor.palette.element_middle,
9208       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9209     },
9210     {
9211       &editor.button.element_right,     &editor.palette.element_right,
9212       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9213     },
9214
9215     { NULL,                     NULL                    }
9216   };
9217   int i;
9218
9219   /* set default position for element buttons to element graphics */
9220   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9221   {
9222     if ((*editor_buttons_xy[i].dst).x == -1 &&
9223         (*editor_buttons_xy[i].dst).y == -1)
9224     {
9225       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9226
9227       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9228
9229       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9230     }
9231   }
9232 }
9233
9234 static void LoadMenuDesignSettingsFromFilename(char *filename)
9235 {
9236   static struct TitleFadingInfo tfi;
9237   static struct TitleMessageInfo tmi;
9238   static struct TokenInfo title_tokens[] =
9239   {
9240     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9241     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9242     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9243     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9244
9245     { -1,               NULL,                   NULL                    }
9246   };
9247   static struct TokenInfo titlemessage_tokens[] =
9248   {
9249     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9250     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9251     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9252     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9253     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9254     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9255     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9256     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9257     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9258     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9259     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9260     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9261     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9262     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9263     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9264     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9265     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9266
9267     { -1,               NULL,                   NULL                    }
9268   };
9269   static struct
9270   {
9271     struct TitleFadingInfo *info;
9272     char *text;
9273   }
9274   title_info[] =
9275   {
9276     /* initialize first titles from "enter screen" definitions, if defined */
9277     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9278     { &title_first_default,             "menu.enter_screen.TITLE"       },
9279
9280     /* initialize title screens from "next screen" definitions, if defined */
9281     { &title_initial_default,           "menu.next_screen.TITLE"        },
9282     { &title_default,                   "menu.next_screen.TITLE"        },
9283
9284     { NULL,                             NULL                            }
9285   };
9286   static struct
9287   {
9288     struct TitleMessageInfo *array;
9289     char *text;
9290   }
9291   titlemessage_arrays[] =
9292   {
9293     /* initialize first titles from "enter screen" definitions, if defined */
9294     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9295     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9296     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9297     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9298
9299     /* initialize titles from "next screen" definitions, if defined */
9300     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9301     { titlescreen,                      "menu.next_screen.TITLE"        },
9302     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9303     { titlemessage,                     "menu.next_screen.TITLE"        },
9304
9305     /* overwrite titles with title definitions, if defined */
9306     { titlescreen_initial_first,        "[title_initial]"               },
9307     { titlescreen_first,                "[title]"                       },
9308     { titlemessage_initial_first,       "[title_initial]"               },
9309     { titlemessage_first,               "[title]"                       },
9310
9311     { titlescreen_initial,              "[title_initial]"               },
9312     { titlescreen,                      "[title]"                       },
9313     { titlemessage_initial,             "[title_initial]"               },
9314     { titlemessage,                     "[title]"                       },
9315
9316     /* overwrite titles with title screen/message definitions, if defined */
9317     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9318     { titlescreen_first,                "[titlescreen]"                 },
9319     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9320     { titlemessage_first,               "[titlemessage]"                },
9321
9322     { titlescreen_initial,              "[titlescreen_initial]"         },
9323     { titlescreen,                      "[titlescreen]"                 },
9324     { titlemessage_initial,             "[titlemessage_initial]"        },
9325     { titlemessage,                     "[titlemessage]"                },
9326
9327     { NULL,                             NULL                            }
9328   };
9329   SetupFileHash *setup_file_hash;
9330   int i, j, k;
9331
9332   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9333     return;
9334
9335   /* the following initializes hierarchical values from dynamic configuration */
9336
9337   /* special case: initialize with default values that may be overwritten */
9338   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9339   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9340   {
9341     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9342     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9343     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9344
9345     if (value_1 != NULL)
9346       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9347     if (value_2 != NULL)
9348       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9349     if (value_3 != NULL)
9350       menu.list_size[i] = get_integer_from_string(value_3);
9351   }
9352
9353   /* special case: initialize with default values that may be overwritten */
9354   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9355   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9356   {
9357     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9358     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9359
9360     if (value_1 != NULL)
9361       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9362     if (value_2 != NULL)
9363       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9364
9365     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9366     {
9367       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9368
9369       if (value_1 != NULL)
9370         menu.list_size_info[i] = get_integer_from_string(value_1);
9371     }
9372   }
9373
9374   /* special case: initialize with default values that may be overwritten */
9375   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9376   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9377   {
9378     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9379     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9380
9381     if (value_1 != NULL)
9382       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9383     if (value_2 != NULL)
9384       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9385   }
9386
9387   /* special case: initialize with default values that may be overwritten */
9388   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9389   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9390   {
9391     char *token_1 = "menu.enter_screen.fade_mode";
9392     char *token_2 = "menu.enter_screen.fade_delay";
9393     char *token_3 = "menu.enter_screen.post_delay";
9394     char *token_4 = "menu.leave_screen.fade_mode";
9395     char *token_5 = "menu.leave_screen.fade_delay";
9396     char *token_6 = "menu.leave_screen.post_delay";
9397     char *token_7 = "menu.next_screen.fade_mode";
9398     char *token_8 = "menu.next_screen.fade_delay";
9399     char *token_9 = "menu.next_screen.post_delay";
9400     char *value_1 = getHashEntry(setup_file_hash, token_1);
9401     char *value_2 = getHashEntry(setup_file_hash, token_2);
9402     char *value_3 = getHashEntry(setup_file_hash, token_3);
9403     char *value_4 = getHashEntry(setup_file_hash, token_4);
9404     char *value_5 = getHashEntry(setup_file_hash, token_5);
9405     char *value_6 = getHashEntry(setup_file_hash, token_6);
9406     char *value_7 = getHashEntry(setup_file_hash, token_7);
9407     char *value_8 = getHashEntry(setup_file_hash, token_8);
9408     char *value_9 = getHashEntry(setup_file_hash, token_9);
9409
9410     if (value_1 != NULL)
9411       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9412                                                                  value_1);
9413     if (value_2 != NULL)
9414       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9415                                                                   value_2);
9416     if (value_3 != NULL)
9417       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9418                                                                   value_3);
9419     if (value_4 != NULL)
9420       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9421                                                                  value_4);
9422     if (value_5 != NULL)
9423       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9424                                                                   value_5);
9425     if (value_6 != NULL)
9426       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9427                                                                   value_6);
9428     if (value_7 != NULL)
9429       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9430                                                                 value_7);
9431     if (value_8 != NULL)
9432       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9433                                                                  value_8);
9434     if (value_9 != NULL)
9435       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9436                                                                  value_9);
9437   }
9438
9439   /* special case: initialize with default values that may be overwritten */
9440   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9441   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9442   {
9443     char *token_w1 = "viewport.window.width";
9444     char *token_w2 = "viewport.window.height";
9445     char *token_01 = "viewport.playfield.x";
9446     char *token_02 = "viewport.playfield.y";
9447     char *token_03 = "viewport.playfield.width";
9448     char *token_04 = "viewport.playfield.height";
9449     char *token_05 = "viewport.playfield.border_size";
9450     char *token_06 = "viewport.door_1.x";
9451     char *token_07 = "viewport.door_1.y";
9452     char *token_08 = "viewport.door_1.width";
9453     char *token_09 = "viewport.door_1.height";
9454     char *token_10 = "viewport.door_1.border_size";
9455     char *token_11 = "viewport.door_2.x";
9456     char *token_12 = "viewport.door_2.y";
9457     char *token_13 = "viewport.door_2.width";
9458     char *token_14 = "viewport.door_2.height";
9459     char *token_15 = "viewport.door_2.border_size";
9460     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9461     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9462     char *value_01 = getHashEntry(setup_file_hash, token_01);
9463     char *value_02 = getHashEntry(setup_file_hash, token_02);
9464     char *value_03 = getHashEntry(setup_file_hash, token_03);
9465     char *value_04 = getHashEntry(setup_file_hash, token_04);
9466     char *value_05 = getHashEntry(setup_file_hash, token_05);
9467     char *value_06 = getHashEntry(setup_file_hash, token_06);
9468     char *value_07 = getHashEntry(setup_file_hash, token_07);
9469     char *value_08 = getHashEntry(setup_file_hash, token_08);
9470     char *value_09 = getHashEntry(setup_file_hash, token_09);
9471     char *value_10 = getHashEntry(setup_file_hash, token_10);
9472     char *value_11 = getHashEntry(setup_file_hash, token_11);
9473     char *value_12 = getHashEntry(setup_file_hash, token_12);
9474     char *value_13 = getHashEntry(setup_file_hash, token_13);
9475     char *value_14 = getHashEntry(setup_file_hash, token_14);
9476     char *value_15 = getHashEntry(setup_file_hash, token_15);
9477
9478     if (value_w1 != NULL)
9479       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9480     if (value_w2 != NULL)
9481       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9482     if (value_01 != NULL)
9483       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9484     if (value_02 != NULL)
9485       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9486     if (value_03 != NULL)
9487       viewport.playfield[i].width = get_token_parameter_value(token_03,
9488                                                               value_03);
9489     if (value_04 != NULL)
9490       viewport.playfield[i].height = get_token_parameter_value(token_04,
9491                                                                value_04);
9492     if (value_05 != NULL)
9493       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9494                                                                     value_05);
9495     if (value_06 != NULL)
9496       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9497     if (value_07 != NULL)
9498       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9499     if (value_08 != NULL)
9500       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9501     if (value_09 != NULL)
9502       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9503     if (value_10 != NULL)
9504       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9505                                                                  value_10);
9506     if (value_11 != NULL)
9507       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9508     if (value_12 != NULL)
9509       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9510     if (value_13 != NULL)
9511       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9512     if (value_14 != NULL)
9513       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9514     if (value_15 != NULL)
9515       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9516                                                                  value_15);
9517   }
9518
9519   /* special case: initialize with default values that may be overwritten */
9520   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9521   for (i = 0; title_info[i].info != NULL; i++)
9522   {
9523     struct TitleFadingInfo *info = title_info[i].info;
9524     char *base_token = title_info[i].text;
9525
9526     for (j = 0; title_tokens[j].type != -1; j++)
9527     {
9528       char *token = getStringCat2(base_token, title_tokens[j].text);
9529       char *value = getHashEntry(setup_file_hash, token);
9530
9531       if (value != NULL)
9532       {
9533         int parameter_value = get_token_parameter_value(token, value);
9534
9535         tfi = *info;
9536
9537         *(int *)title_tokens[j].value = (int)parameter_value;
9538
9539         *info = tfi;
9540       }
9541
9542       free(token);
9543     }
9544   }
9545
9546   /* special case: initialize with default values that may be overwritten */
9547   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9548   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9549   {
9550     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9551     char *base_token = titlemessage_arrays[i].text;
9552
9553     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9554     {
9555       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9556       char *value = getHashEntry(setup_file_hash, token);
9557
9558       if (value != NULL)
9559       {
9560         int parameter_value = get_token_parameter_value(token, value);
9561
9562         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9563         {
9564           tmi = array[k];
9565
9566           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9567             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9568           else
9569             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9570
9571           array[k] = tmi;
9572         }
9573       }
9574
9575       free(token);
9576     }
9577   }
9578
9579   /* read (and overwrite with) values that may be specified in config file */
9580   for (i = 0; image_config_vars[i].token != NULL; i++)
9581   {
9582     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9583
9584     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9585     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9586       *image_config_vars[i].value =
9587         get_token_parameter_value(image_config_vars[i].token, value);
9588   }
9589
9590   freeSetupFileHash(setup_file_hash);
9591 }
9592
9593 void LoadMenuDesignSettings()
9594 {
9595   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9596
9597   InitMenuDesignSettings_Static();
9598   InitMenuDesignSettings_SpecialPreProcessing();
9599
9600   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9601   {
9602     /* first look for special settings configured in level series config */
9603     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9604
9605     if (fileExists(filename_base))
9606       LoadMenuDesignSettingsFromFilename(filename_base);
9607   }
9608
9609   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9610
9611   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9612     LoadMenuDesignSettingsFromFilename(filename_local);
9613
9614   InitMenuDesignSettings_SpecialPostProcessing();
9615 }
9616
9617 void LoadMenuDesignSettings_AfterGraphics()
9618 {
9619   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9620 }
9621
9622 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9623 {
9624   char *filename = getEditorSetupFilename();
9625   SetupFileList *setup_file_list, *list;
9626   SetupFileHash *element_hash;
9627   int num_unknown_tokens = 0;
9628   int i;
9629
9630   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9631     return;
9632
9633   element_hash = newSetupFileHash();
9634
9635   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9636     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9637
9638   /* determined size may be larger than needed (due to unknown elements) */
9639   *num_elements = 0;
9640   for (list = setup_file_list; list != NULL; list = list->next)
9641     (*num_elements)++;
9642
9643   /* add space for up to 3 more elements for padding that may be needed */
9644   *num_elements += 3;
9645
9646   /* free memory for old list of elements, if needed */
9647   checked_free(*elements);
9648
9649   /* allocate memory for new list of elements */
9650   *elements = checked_malloc(*num_elements * sizeof(int));
9651
9652   *num_elements = 0;
9653   for (list = setup_file_list; list != NULL; list = list->next)
9654   {
9655     char *value = getHashEntry(element_hash, list->token);
9656
9657     if (value == NULL)          /* try to find obsolete token mapping */
9658     {
9659       char *mapped_token = get_mapped_token(list->token);
9660
9661       if (mapped_token != NULL)
9662       {
9663         value = getHashEntry(element_hash, mapped_token);
9664
9665         free(mapped_token);
9666       }
9667     }
9668
9669     if (value != NULL)
9670     {
9671       (*elements)[(*num_elements)++] = atoi(value);
9672     }
9673     else
9674     {
9675       if (num_unknown_tokens == 0)
9676       {
9677         Error(ERR_INFO_LINE, "-");
9678         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9679         Error(ERR_INFO, "- config file: '%s'", filename);
9680
9681         num_unknown_tokens++;
9682       }
9683
9684       Error(ERR_INFO, "- token: '%s'", list->token);
9685     }
9686   }
9687
9688   if (num_unknown_tokens > 0)
9689     Error(ERR_INFO_LINE, "-");
9690
9691   while (*num_elements % 4)     /* pad with empty elements, if needed */
9692     (*elements)[(*num_elements)++] = EL_EMPTY;
9693
9694   freeSetupFileList(setup_file_list);
9695   freeSetupFileHash(element_hash);
9696
9697 #if 0
9698   for (i = 0; i < *num_elements; i++)
9699     printf("editor: element '%s' [%d]\n",
9700            element_info[(*elements)[i]].token_name, (*elements)[i]);
9701 #endif
9702 }
9703
9704 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9705                                                      boolean is_sound)
9706 {
9707   SetupFileHash *setup_file_hash = NULL;
9708   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9709   char *filename_music, *filename_prefix, *filename_info;
9710   struct
9711   {
9712     char *token;
9713     char **value_ptr;
9714   }
9715   token_to_value_ptr[] =
9716   {
9717     { "title_header",   &tmp_music_file_info.title_header       },
9718     { "artist_header",  &tmp_music_file_info.artist_header      },
9719     { "album_header",   &tmp_music_file_info.album_header       },
9720     { "year_header",    &tmp_music_file_info.year_header        },
9721
9722     { "title",          &tmp_music_file_info.title              },
9723     { "artist",         &tmp_music_file_info.artist             },
9724     { "album",          &tmp_music_file_info.album              },
9725     { "year",           &tmp_music_file_info.year               },
9726
9727     { NULL,             NULL                                    },
9728   };
9729   int i;
9730
9731   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9732                     getCustomMusicFilename(basename));
9733
9734   if (filename_music == NULL)
9735     return NULL;
9736
9737   /* ---------- try to replace file extension ---------- */
9738
9739   filename_prefix = getStringCopy(filename_music);
9740   if (strrchr(filename_prefix, '.') != NULL)
9741     *strrchr(filename_prefix, '.') = '\0';
9742   filename_info = getStringCat2(filename_prefix, ".txt");
9743
9744   if (fileExists(filename_info))
9745     setup_file_hash = loadSetupFileHash(filename_info);
9746
9747   free(filename_prefix);
9748   free(filename_info);
9749
9750   if (setup_file_hash == NULL)
9751   {
9752     /* ---------- try to add file extension ---------- */
9753
9754     filename_prefix = getStringCopy(filename_music);
9755     filename_info = getStringCat2(filename_prefix, ".txt");
9756
9757     if (fileExists(filename_info))
9758       setup_file_hash = loadSetupFileHash(filename_info);
9759
9760     free(filename_prefix);
9761     free(filename_info);
9762   }
9763
9764   if (setup_file_hash == NULL)
9765     return NULL;
9766
9767   /* ---------- music file info found ---------- */
9768
9769   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9770
9771   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9772   {
9773     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9774
9775     *token_to_value_ptr[i].value_ptr =
9776       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9777   }
9778
9779   tmp_music_file_info.basename = getStringCopy(basename);
9780   tmp_music_file_info.music = music;
9781   tmp_music_file_info.is_sound = is_sound;
9782
9783   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9784   *new_music_file_info = tmp_music_file_info;
9785
9786   return new_music_file_info;
9787 }
9788
9789 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9790 {
9791   return get_music_file_info_ext(basename, music, FALSE);
9792 }
9793
9794 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9795 {
9796   return get_music_file_info_ext(basename, sound, TRUE);
9797 }
9798
9799 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9800                                      char *basename, boolean is_sound)
9801 {
9802   for (; list != NULL; list = list->next)
9803     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9804       return TRUE;
9805
9806   return FALSE;
9807 }
9808
9809 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9810 {
9811   return music_info_listed_ext(list, basename, FALSE);
9812 }
9813
9814 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
9815 {
9816   return music_info_listed_ext(list, basename, TRUE);
9817 }
9818
9819 void LoadMusicInfo()
9820 {
9821   char *music_directory = getCustomMusicDirectory();
9822   int num_music = getMusicListSize();
9823   int num_music_noconf = 0;
9824   int num_sounds = getSoundListSize();
9825   Directory *dir;
9826   DirectoryEntry *dir_entry;
9827   struct FileInfo *music, *sound;
9828   struct MusicFileInfo *next, **new;
9829   int i;
9830
9831   while (music_file_info != NULL)
9832   {
9833     next = music_file_info->next;
9834
9835     checked_free(music_file_info->basename);
9836
9837     checked_free(music_file_info->title_header);
9838     checked_free(music_file_info->artist_header);
9839     checked_free(music_file_info->album_header);
9840     checked_free(music_file_info->year_header);
9841
9842     checked_free(music_file_info->title);
9843     checked_free(music_file_info->artist);
9844     checked_free(music_file_info->album);
9845     checked_free(music_file_info->year);
9846
9847     free(music_file_info);
9848
9849     music_file_info = next;
9850   }
9851
9852   new = &music_file_info;
9853
9854   for (i = 0; i < num_music; i++)
9855   {
9856     music = getMusicListEntry(i);
9857
9858     if (music->filename == NULL)
9859       continue;
9860
9861     if (strEqual(music->filename, UNDEFINED_FILENAME))
9862       continue;
9863
9864     /* a configured file may be not recognized as music */
9865     if (!FileIsMusic(music->filename))
9866       continue;
9867
9868     if (!music_info_listed(music_file_info, music->filename))
9869     {
9870       *new = get_music_file_info(music->filename, i);
9871
9872       if (*new != NULL)
9873         new = &(*new)->next;
9874     }
9875   }
9876
9877   if ((dir = openDirectory(music_directory)) == NULL)
9878   {
9879     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
9880     return;
9881   }
9882
9883   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
9884   {
9885     char *basename = dir_entry->basename;
9886     boolean music_already_used = FALSE;
9887     int i;
9888
9889     /* skip all music files that are configured in music config file */
9890     for (i = 0; i < num_music; i++)
9891     {
9892       music = getMusicListEntry(i);
9893
9894       if (music->filename == NULL)
9895         continue;
9896
9897       if (strEqual(basename, music->filename))
9898       {
9899         music_already_used = TRUE;
9900         break;
9901       }
9902     }
9903
9904     if (music_already_used)
9905       continue;
9906
9907     if (!FileIsMusic(dir_entry->filename))
9908       continue;
9909
9910     if (!music_info_listed(music_file_info, basename))
9911     {
9912       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
9913
9914       if (*new != NULL)
9915         new = &(*new)->next;
9916     }
9917
9918     num_music_noconf++;
9919   }
9920
9921   closeDirectory(dir);
9922
9923   for (i = 0; i < num_sounds; i++)
9924   {
9925     sound = getSoundListEntry(i);
9926
9927     if (sound->filename == NULL)
9928       continue;
9929
9930     if (strEqual(sound->filename, UNDEFINED_FILENAME))
9931       continue;
9932
9933     /* a configured file may be not recognized as sound */
9934     if (!FileIsSound(sound->filename))
9935       continue;
9936
9937     if (!sound_info_listed(music_file_info, sound->filename))
9938     {
9939       *new = get_sound_file_info(sound->filename, i);
9940       if (*new != NULL)
9941         new = &(*new)->next;
9942     }
9943   }
9944 }
9945
9946 void add_helpanim_entry(int element, int action, int direction, int delay,
9947                         int *num_list_entries)
9948 {
9949   struct HelpAnimInfo *new_list_entry;
9950   (*num_list_entries)++;
9951
9952   helpanim_info =
9953     checked_realloc(helpanim_info,
9954                     *num_list_entries * sizeof(struct HelpAnimInfo));
9955   new_list_entry = &helpanim_info[*num_list_entries - 1];
9956
9957   new_list_entry->element = element;
9958   new_list_entry->action = action;
9959   new_list_entry->direction = direction;
9960   new_list_entry->delay = delay;
9961 }
9962
9963 void print_unknown_token(char *filename, char *token, int token_nr)
9964 {
9965   if (token_nr == 0)
9966   {
9967     Error(ERR_INFO_LINE, "-");
9968     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9969     Error(ERR_INFO, "- config file: '%s'", filename);
9970   }
9971
9972   Error(ERR_INFO, "- token: '%s'", token);
9973 }
9974
9975 void print_unknown_token_end(int token_nr)
9976 {
9977   if (token_nr > 0)
9978     Error(ERR_INFO_LINE, "-");
9979 }
9980
9981 void LoadHelpAnimInfo()
9982 {
9983   char *filename = getHelpAnimFilename();
9984   SetupFileList *setup_file_list = NULL, *list;
9985   SetupFileHash *element_hash, *action_hash, *direction_hash;
9986   int num_list_entries = 0;
9987   int num_unknown_tokens = 0;
9988   int i;
9989
9990   if (fileExists(filename))
9991     setup_file_list = loadSetupFileList(filename);
9992
9993   if (setup_file_list == NULL)
9994   {
9995     /* use reliable default values from static configuration */
9996     SetupFileList *insert_ptr;
9997
9998     insert_ptr = setup_file_list =
9999       newSetupFileList(helpanim_config[0].token,
10000                        helpanim_config[0].value);
10001
10002     for (i = 1; helpanim_config[i].token; i++)
10003       insert_ptr = addListEntry(insert_ptr,
10004                                 helpanim_config[i].token,
10005                                 helpanim_config[i].value);
10006   }
10007
10008   element_hash   = newSetupFileHash();
10009   action_hash    = newSetupFileHash();
10010   direction_hash = newSetupFileHash();
10011
10012   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10013     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10014
10015   for (i = 0; i < NUM_ACTIONS; i++)
10016     setHashEntry(action_hash, element_action_info[i].suffix,
10017                  i_to_a(element_action_info[i].value));
10018
10019   /* do not store direction index (bit) here, but direction value! */
10020   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10021     setHashEntry(direction_hash, element_direction_info[i].suffix,
10022                  i_to_a(1 << element_direction_info[i].value));
10023
10024   for (list = setup_file_list; list != NULL; list = list->next)
10025   {
10026     char *element_token, *action_token, *direction_token;
10027     char *element_value, *action_value, *direction_value;
10028     int delay = atoi(list->value);
10029
10030     if (strEqual(list->token, "end"))
10031     {
10032       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10033
10034       continue;
10035     }
10036
10037     /* first try to break element into element/action/direction parts;
10038        if this does not work, also accept combined "element[.act][.dir]"
10039        elements (like "dynamite.active"), which are unique elements */
10040
10041     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10042     {
10043       element_value = getHashEntry(element_hash, list->token);
10044       if (element_value != NULL)        /* element found */
10045         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10046                            &num_list_entries);
10047       else
10048       {
10049         /* no further suffixes found -- this is not an element */
10050         print_unknown_token(filename, list->token, num_unknown_tokens++);
10051       }
10052
10053       continue;
10054     }
10055
10056     /* token has format "<prefix>.<something>" */
10057
10058     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10059     direction_token = action_token;             /* ... or direction */
10060
10061     element_token = getStringCopy(list->token);
10062     *strchr(element_token, '.') = '\0';
10063
10064     element_value = getHashEntry(element_hash, element_token);
10065
10066     if (element_value == NULL)          /* this is no element */
10067     {
10068       element_value = getHashEntry(element_hash, list->token);
10069       if (element_value != NULL)        /* combined element found */
10070         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10071                            &num_list_entries);
10072       else
10073         print_unknown_token(filename, list->token, num_unknown_tokens++);
10074
10075       free(element_token);
10076
10077       continue;
10078     }
10079
10080     action_value = getHashEntry(action_hash, action_token);
10081
10082     if (action_value != NULL)           /* action found */
10083     {
10084       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10085                     &num_list_entries);
10086
10087       free(element_token);
10088
10089       continue;
10090     }
10091
10092     direction_value = getHashEntry(direction_hash, direction_token);
10093
10094     if (direction_value != NULL)        /* direction found */
10095     {
10096       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10097                          &num_list_entries);
10098
10099       free(element_token);
10100
10101       continue;
10102     }
10103
10104     if (strchr(action_token + 1, '.') == NULL)
10105     {
10106       /* no further suffixes found -- this is not an action nor direction */
10107
10108       element_value = getHashEntry(element_hash, list->token);
10109       if (element_value != NULL)        /* combined element found */
10110         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10111                            &num_list_entries);
10112       else
10113         print_unknown_token(filename, list->token, num_unknown_tokens++);
10114
10115       free(element_token);
10116
10117       continue;
10118     }
10119
10120     /* token has format "<prefix>.<suffix>.<something>" */
10121
10122     direction_token = strchr(action_token + 1, '.');
10123
10124     action_token = getStringCopy(action_token);
10125     *strchr(action_token + 1, '.') = '\0';
10126
10127     action_value = getHashEntry(action_hash, action_token);
10128
10129     if (action_value == NULL)           /* this is no action */
10130     {
10131       element_value = getHashEntry(element_hash, list->token);
10132       if (element_value != NULL)        /* combined element found */
10133         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10134                            &num_list_entries);
10135       else
10136         print_unknown_token(filename, list->token, num_unknown_tokens++);
10137
10138       free(element_token);
10139       free(action_token);
10140
10141       continue;
10142     }
10143
10144     direction_value = getHashEntry(direction_hash, direction_token);
10145
10146     if (direction_value != NULL)        /* direction found */
10147     {
10148       add_helpanim_entry(atoi(element_value), atoi(action_value),
10149                          atoi(direction_value), delay, &num_list_entries);
10150
10151       free(element_token);
10152       free(action_token);
10153
10154       continue;
10155     }
10156
10157     /* this is no direction */
10158
10159     element_value = getHashEntry(element_hash, list->token);
10160     if (element_value != NULL)          /* combined element found */
10161       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10162                          &num_list_entries);
10163     else
10164       print_unknown_token(filename, list->token, num_unknown_tokens++);
10165
10166     free(element_token);
10167     free(action_token);
10168   }
10169
10170   print_unknown_token_end(num_unknown_tokens);
10171
10172   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10173   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10174
10175   freeSetupFileList(setup_file_list);
10176   freeSetupFileHash(element_hash);
10177   freeSetupFileHash(action_hash);
10178   freeSetupFileHash(direction_hash);
10179
10180 #if 0
10181   for (i = 0; i < num_list_entries; i++)
10182     printf("::: '%s': %d, %d, %d => %d\n",
10183            EL_NAME(helpanim_info[i].element),
10184            helpanim_info[i].element,
10185            helpanim_info[i].action,
10186            helpanim_info[i].direction,
10187            helpanim_info[i].delay);
10188 #endif
10189 }
10190
10191 void LoadHelpTextInfo()
10192 {
10193   char *filename = getHelpTextFilename();
10194   int i;
10195
10196   if (helptext_info != NULL)
10197   {
10198     freeSetupFileHash(helptext_info);
10199     helptext_info = NULL;
10200   }
10201
10202   if (fileExists(filename))
10203     helptext_info = loadSetupFileHash(filename);
10204
10205   if (helptext_info == NULL)
10206   {
10207     /* use reliable default values from static configuration */
10208     helptext_info = newSetupFileHash();
10209
10210     for (i = 0; helptext_config[i].token; i++)
10211       setHashEntry(helptext_info,
10212                    helptext_config[i].token,
10213                    helptext_config[i].value);
10214   }
10215
10216 #if 0
10217   BEGIN_HASH_ITERATION(helptext_info, itr)
10218   {
10219     printf("::: '%s' => '%s'\n",
10220            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10221   }
10222   END_HASH_ITERATION(hash, itr)
10223 #endif
10224 }
10225
10226
10227 /* ------------------------------------------------------------------------- */
10228 /* convert levels                                                            */
10229 /* ------------------------------------------------------------------------- */
10230
10231 #define MAX_NUM_CONVERT_LEVELS          1000
10232
10233 void ConvertLevels()
10234 {
10235   static LevelDirTree *convert_leveldir = NULL;
10236   static int convert_level_nr = -1;
10237   static int num_levels_handled = 0;
10238   static int num_levels_converted = 0;
10239   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10240   int i;
10241
10242   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10243                                                global.convert_leveldir);
10244
10245   if (convert_leveldir == NULL)
10246     Error(ERR_EXIT, "no such level identifier: '%s'",
10247           global.convert_leveldir);
10248
10249   leveldir_current = convert_leveldir;
10250
10251   if (global.convert_level_nr != -1)
10252   {
10253     convert_leveldir->first_level = global.convert_level_nr;
10254     convert_leveldir->last_level  = global.convert_level_nr;
10255   }
10256
10257   convert_level_nr = convert_leveldir->first_level;
10258
10259   PrintLine("=", 79);
10260   Print("Converting levels\n");
10261   PrintLine("-", 79);
10262   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10263   Print("Level series name:       '%s'\n", convert_leveldir->name);
10264   Print("Level series author:     '%s'\n", convert_leveldir->author);
10265   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10266   PrintLine("=", 79);
10267   Print("\n");
10268
10269   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10270     levels_failed[i] = FALSE;
10271
10272   while (convert_level_nr <= convert_leveldir->last_level)
10273   {
10274     char *level_filename;
10275     boolean new_level;
10276
10277     level_nr = convert_level_nr++;
10278
10279     Print("Level %03d: ", level_nr);
10280
10281     LoadLevel(level_nr);
10282     if (level.no_level_file || level.no_valid_file)
10283     {
10284       Print("(no level)\n");
10285       continue;
10286     }
10287
10288     Print("converting level ... ");
10289
10290     level_filename = getDefaultLevelFilename(level_nr);
10291     new_level = !fileExists(level_filename);
10292
10293     if (new_level)
10294     {
10295       SaveLevel(level_nr);
10296
10297       num_levels_converted++;
10298
10299       Print("converted.\n");
10300     }
10301     else
10302     {
10303       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10304         levels_failed[level_nr] = TRUE;
10305
10306       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10307     }
10308
10309     num_levels_handled++;
10310   }
10311
10312   Print("\n");
10313   PrintLine("=", 79);
10314   Print("Number of levels handled: %d\n", num_levels_handled);
10315   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10316          (num_levels_handled ?
10317           num_levels_converted * 100 / num_levels_handled : 0));
10318   PrintLine("-", 79);
10319   Print("Summary (for automatic parsing by scripts):\n");
10320   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10321          convert_leveldir->identifier, num_levels_converted,
10322          num_levels_handled,
10323          (num_levels_handled ?
10324           num_levels_converted * 100 / num_levels_handled : 0));
10325
10326   if (num_levels_handled != num_levels_converted)
10327   {
10328     Print(", FAILED:");
10329     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10330       if (levels_failed[i])
10331         Print(" %03d", i);
10332   }
10333
10334   Print("\n");
10335   PrintLine("=", 79);
10336
10337   CloseAllAndExit(0);
10338 }
10339
10340
10341 /* ------------------------------------------------------------------------- */
10342 /* create and save images for use in level sketches (raw BMP format)         */
10343 /* ------------------------------------------------------------------------- */
10344
10345 void CreateLevelSketchImages()
10346 {
10347 #if defined(TARGET_SDL)
10348   Bitmap *bitmap1;
10349   Bitmap *bitmap2;
10350   int i;
10351
10352   InitElementPropertiesGfxElement();
10353
10354   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10355   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10356
10357   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10358   {
10359     Bitmap *src_bitmap;
10360     int src_x, src_y;
10361     int element = getMappedElement(i);
10362     int graphic = el2edimg(element);
10363     char basename1[16];
10364     char basename2[16];
10365     char *filename1;
10366     char *filename2;
10367
10368     sprintf(basename1, "%03d.bmp", i);
10369     sprintf(basename2, "%03ds.bmp", i);
10370
10371     filename1 = getPath2(global.create_images_dir, basename1);
10372     filename2 = getPath2(global.create_images_dir, basename2);
10373
10374     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10375     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10376                0, 0);
10377
10378     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10379       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10380
10381     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10382     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10383
10384     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10385       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10386
10387     free(filename1);
10388     free(filename2);
10389
10390     if (options.debug)
10391       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10392   }
10393
10394   FreeBitmap(bitmap1);
10395   FreeBitmap(bitmap2);
10396
10397   if (options.debug)
10398     printf("\n");
10399
10400   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10401
10402   CloseAllAndExit(0);
10403 #endif
10404 }
10405
10406
10407 /* ------------------------------------------------------------------------- */
10408 /* create and save images for custom and group elements (raw BMP format)     */
10409 /* ------------------------------------------------------------------------- */
10410
10411 void CreateCustomElementImages(char *directory)
10412 {
10413 #if defined(TARGET_SDL)
10414   char *src_basename = "RocksCE-template.ilbm";
10415   char *dst_basename = "RocksCE.bmp";
10416   char *src_filename = getPath2(directory, src_basename);
10417   char *dst_filename = getPath2(directory, dst_basename);
10418   Bitmap *src_bitmap;
10419   Bitmap *bitmap;
10420   int yoffset_ce = 0;
10421   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10422   int i;
10423
10424   SDLInitVideoDisplay();
10425
10426   ReCreateBitmap(&backbuffer, video.width, video.height);
10427
10428   src_bitmap = LoadImage(src_filename);
10429
10430   bitmap = CreateBitmap(TILEX * 16 * 2,
10431                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10432                         DEFAULT_DEPTH);
10433
10434   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10435   {
10436     int x = i % 16;
10437     int y = i / 16;
10438     int ii = i + 1;
10439     int j;
10440
10441     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10442                TILEX * x, TILEY * y + yoffset_ce);
10443
10444     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10445                TILEX, TILEY,
10446                TILEX * x + TILEX * 16,
10447                TILEY * y + yoffset_ce);
10448
10449     for (j = 2; j >= 0; j--)
10450     {
10451       int c = ii % 10;
10452
10453       BlitBitmap(src_bitmap, bitmap,
10454                  TILEX + c * 7, 0, 6, 10,
10455                  TILEX * x + 6 + j * 7,
10456                  TILEY * y + 11 + yoffset_ce);
10457
10458       BlitBitmap(src_bitmap, bitmap,
10459                  TILEX + c * 8, TILEY, 6, 10,
10460                  TILEX * 16 + TILEX * x + 6 + j * 8,
10461                  TILEY * y + 10 + yoffset_ce);
10462
10463       ii /= 10;
10464     }
10465   }
10466
10467   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10468   {
10469     int x = i % 16;
10470     int y = i / 16;
10471     int ii = i + 1;
10472     int j;
10473
10474     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10475                TILEX * x, TILEY * y + yoffset_ge);
10476
10477     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10478                TILEX, TILEY,
10479                TILEX * x + TILEX * 16,
10480                TILEY * y + yoffset_ge);
10481
10482     for (j = 1; j >= 0; j--)
10483     {
10484       int c = ii % 10;
10485
10486       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10487                  TILEX * x + 6 + j * 10,
10488                  TILEY * y + 11 + yoffset_ge);
10489
10490       BlitBitmap(src_bitmap, bitmap,
10491                  TILEX + c * 8, TILEY + 12, 6, 10,
10492                  TILEX * 16 + TILEX * x + 10 + j * 8,
10493                  TILEY * y + 10 + yoffset_ge);
10494
10495       ii /= 10;
10496     }
10497   }
10498
10499   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10500     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10501
10502   FreeBitmap(bitmap);
10503
10504   CloseAllAndExit(0);
10505 #endif
10506 }