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