changed initializing CE properties to older versions only for CE levels
[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
6457 static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
6458 {
6459   LoadLevel_InitStandardElements(level);
6460
6461   if (level->file_has_custom_elements)
6462     LoadLevel_InitCustomElements(level);
6463
6464   if (level->file_has_custom_elements)
6465     InitElementPropertiesAfterLoading(level->game_version);
6466
6467   /* initialize element properties for level editor etc. */
6468   InitElementPropertiesEngine(level->game_version);
6469   InitElementPropertiesGfxElement();
6470 }
6471
6472 static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
6473 {
6474   int x, y;
6475
6476   /* map elements that have changed in newer versions */
6477   for (y = 0; y < level->fieldy; y++)
6478     for (x = 0; x < level->fieldx; x++)
6479       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
6480                                                      level->game_version);
6481
6482   /* clear unused playfield data (nicer if level gets resized in editor) */
6483   for (x = 0; x < MAX_LEV_FIELDX; x++)
6484     for (y = 0; y < MAX_LEV_FIELDY; y++)
6485       if (x >= level->fieldx || y >= level->fieldy)
6486         level->field[x][y] = EL_EMPTY;
6487
6488   /* copy elements to runtime playfield array */
6489   for (x = 0; x < MAX_LEV_FIELDX; x++)
6490     for (y = 0; y < MAX_LEV_FIELDY; y++)
6491       Feld[x][y] = level->field[x][y];
6492
6493   /* initialize level size variables for faster access */
6494   lev_fieldx = level->fieldx;
6495   lev_fieldy = level->fieldy;
6496
6497   /* determine border element for this level */
6498   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
6499     BorderElement = EL_EMPTY;   /* (in editor, SetBorderElement() is used) */
6500   else
6501     SetBorderElement();
6502 }
6503
6504 static void LoadLevel_InitNativeEngines(struct LevelInfo *level,char *filename)
6505 {
6506   struct LevelFileInfo *level_file_info = &level->file_info;
6507
6508   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
6509     CopyNativeLevel_RND_to_Native(level);
6510 }
6511
6512 void LoadLevelTemplate(int nr)
6513 {
6514   char *filename;
6515
6516   setLevelFileInfo(&level_template.file_info, nr);
6517   filename = level_template.file_info.filename;
6518
6519   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
6520
6521   LoadLevel_InitVersion(&level_template, filename);
6522   LoadLevel_InitElements(&level_template, filename);
6523
6524   ActivateLevelTemplate();
6525 }
6526
6527 void LoadLevel(int nr)
6528 {
6529   char *filename;
6530
6531   setLevelFileInfo(&level.file_info, nr);
6532   filename = level.file_info.filename;
6533
6534   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
6535
6536   if (level.use_custom_template)
6537     LoadLevelTemplate(-1);
6538
6539   LoadLevel_InitVersion(&level, filename);
6540   LoadLevel_InitElements(&level, filename);
6541   LoadLevel_InitPlayfield(&level, filename);
6542
6543   LoadLevel_InitNativeEngines(&level, filename);
6544 }
6545
6546 void LoadLevelInfoOnly(int nr)
6547 {
6548   setLevelFileInfo(&level.file_info, nr);
6549
6550   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
6551 }
6552
6553 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
6554 {
6555   int chunk_size = 0;
6556
6557   chunk_size += putFileVersion(file, level->file_version);
6558   chunk_size += putFileVersion(file, level->game_version);
6559
6560   return chunk_size;
6561 }
6562
6563 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
6564 {
6565   int chunk_size = 0;
6566
6567   chunk_size += putFile16BitBE(file, level->creation_date.year);
6568   chunk_size += putFile8Bit(file,    level->creation_date.month);
6569   chunk_size += putFile8Bit(file,    level->creation_date.day);
6570
6571   return chunk_size;
6572 }
6573
6574 #if ENABLE_HISTORIC_CHUNKS
6575 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
6576 {
6577   int i, x, y;
6578
6579   putFile8Bit(file, level->fieldx);
6580   putFile8Bit(file, level->fieldy);
6581
6582   putFile16BitBE(file, level->time);
6583   putFile16BitBE(file, level->gems_needed);
6584
6585   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6586     putFile8Bit(file, level->name[i]);
6587
6588   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
6589     putFile8Bit(file, level->score[i]);
6590
6591   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
6592     for (y = 0; y < 3; y++)
6593       for (x = 0; x < 3; x++)
6594         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
6595                            level->yamyam_content[i].e[x][y]));
6596   putFile8Bit(file, level->amoeba_speed);
6597   putFile8Bit(file, level->time_magic_wall);
6598   putFile8Bit(file, level->time_wheel);
6599   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
6600                      level->amoeba_content));
6601   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
6602   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
6603   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
6604   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
6605
6606   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
6607
6608   putFile8Bit(file, (level->block_last_field ? 1 : 0));
6609   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
6610   putFile32BitBE(file, level->can_move_into_acid_bits);
6611   putFile8Bit(file, level->dont_collide_with_bits);
6612
6613   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
6614   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
6615
6616   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
6617   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
6618   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
6619
6620   putFile8Bit(file, level->game_engine_type);
6621
6622   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
6623 }
6624 #endif
6625
6626 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
6627 {
6628   int chunk_size = 0;
6629   int i;
6630
6631   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
6632     chunk_size += putFile8Bit(file, level->name[i]);
6633
6634   return chunk_size;
6635 }
6636
6637 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
6638 {
6639   int chunk_size = 0;
6640   int i;
6641
6642   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
6643     chunk_size += putFile8Bit(file, level->author[i]);
6644
6645   return chunk_size;
6646 }
6647
6648 #if ENABLE_HISTORIC_CHUNKS
6649 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6650 {
6651   int chunk_size = 0;
6652   int x, y;
6653
6654   for (y = 0; y < level->fieldy; y++) 
6655     for (x = 0; x < level->fieldx; x++) 
6656       if (level->encoding_16bit_field)
6657         chunk_size += putFile16BitBE(file, level->field[x][y]);
6658       else
6659         chunk_size += putFile8Bit(file, level->field[x][y]);
6660
6661   return chunk_size;
6662 }
6663 #endif
6664
6665 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
6666 {
6667   int chunk_size = 0;
6668   int x, y;
6669
6670   for (y = 0; y < level->fieldy; y++) 
6671     for (x = 0; x < level->fieldx; x++) 
6672       chunk_size += putFile16BitBE(file, level->field[x][y]);
6673
6674   return chunk_size;
6675 }
6676
6677 #if ENABLE_HISTORIC_CHUNKS
6678 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
6679 {
6680   int i, x, y;
6681
6682   putFile8Bit(file, EL_YAMYAM);
6683   putFile8Bit(file, level->num_yamyam_contents);
6684   putFile8Bit(file, 0);
6685   putFile8Bit(file, 0);
6686
6687   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6688     for (y = 0; y < 3; y++)
6689       for (x = 0; x < 3; x++)
6690         if (level->encoding_16bit_field)
6691           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
6692         else
6693           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
6694 }
6695 #endif
6696
6697 #if ENABLE_HISTORIC_CHUNKS
6698 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
6699 {
6700   int i, x, y;
6701   int num_contents, content_xsize, content_ysize;
6702   int content_array[MAX_ELEMENT_CONTENTS][3][3];
6703
6704   if (element == EL_YAMYAM)
6705   {
6706     num_contents = level->num_yamyam_contents;
6707     content_xsize = 3;
6708     content_ysize = 3;
6709
6710     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6711       for (y = 0; y < 3; y++)
6712         for (x = 0; x < 3; x++)
6713           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
6714   }
6715   else if (element == EL_BD_AMOEBA)
6716   {
6717     num_contents = 1;
6718     content_xsize = 1;
6719     content_ysize = 1;
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] = EL_EMPTY;
6725     content_array[0][0][0] = level->amoeba_content;
6726   }
6727   else
6728   {
6729     /* chunk header already written -- write empty chunk data */
6730     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
6731
6732     Error(ERR_WARN, "cannot save content for element '%d'", element);
6733     return;
6734   }
6735
6736   putFile16BitBE(file, element);
6737   putFile8Bit(file, num_contents);
6738   putFile8Bit(file, content_xsize);
6739   putFile8Bit(file, content_ysize);
6740
6741   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
6742
6743   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
6744     for (y = 0; y < 3; y++)
6745       for (x = 0; x < 3; x++)
6746         putFile16BitBE(file, content_array[i][x][y]);
6747 }
6748 #endif
6749
6750 #if ENABLE_HISTORIC_CHUNKS
6751 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
6752 {
6753   int envelope_nr = element - EL_ENVELOPE_1;
6754   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
6755   int chunk_size = 0;
6756   int i;
6757
6758   chunk_size += putFile16BitBE(file, element);
6759   chunk_size += putFile16BitBE(file, envelope_len);
6760   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
6761   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
6762
6763   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
6764   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
6765
6766   for (i = 0; i < envelope_len; i++)
6767     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
6768
6769   return chunk_size;
6770 }
6771 #endif
6772
6773 #if ENABLE_HISTORIC_CHUNKS
6774 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
6775                            int num_changed_custom_elements)
6776 {
6777   int i, check = 0;
6778
6779   putFile16BitBE(file, num_changed_custom_elements);
6780
6781   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6782   {
6783     int element = EL_CUSTOM_START + i;
6784
6785     struct ElementInfo *ei = &element_info[element];
6786
6787     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
6788     {
6789       if (check < num_changed_custom_elements)
6790       {
6791         putFile16BitBE(file, element);
6792         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6793       }
6794
6795       check++;
6796     }
6797   }
6798
6799   if (check != num_changed_custom_elements)     /* should not happen */
6800     Error(ERR_WARN, "inconsistent number of custom element properties");
6801 }
6802 #endif
6803
6804 #if ENABLE_HISTORIC_CHUNKS
6805 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
6806                            int num_changed_custom_elements)
6807 {
6808   int i, check = 0;
6809
6810   putFile16BitBE(file, num_changed_custom_elements);
6811
6812   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6813   {
6814     int element = EL_CUSTOM_START + i;
6815
6816     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
6817     {
6818       if (check < num_changed_custom_elements)
6819       {
6820         putFile16BitBE(file, element);
6821         putFile16BitBE(file, element_info[element].change->target_element);
6822       }
6823
6824       check++;
6825     }
6826   }
6827
6828   if (check != num_changed_custom_elements)     /* should not happen */
6829     Error(ERR_WARN, "inconsistent number of custom target elements");
6830 }
6831 #endif
6832
6833 #if ENABLE_HISTORIC_CHUNKS
6834 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
6835                            int num_changed_custom_elements)
6836 {
6837   int i, j, x, y, check = 0;
6838
6839   putFile16BitBE(file, num_changed_custom_elements);
6840
6841   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
6842   {
6843     int element = EL_CUSTOM_START + i;
6844     struct ElementInfo *ei = &element_info[element];
6845
6846     if (ei->modified_settings)
6847     {
6848       if (check < num_changed_custom_elements)
6849       {
6850         putFile16BitBE(file, element);
6851
6852         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
6853           putFile8Bit(file, ei->description[j]);
6854
6855         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6856
6857         /* some free bytes for future properties and padding */
6858         WriteUnusedBytesToFile(file, 7);
6859
6860         putFile8Bit(file, ei->use_gfx_element);
6861         putFile16BitBE(file, ei->gfx_element_initial);
6862
6863         putFile8Bit(file, ei->collect_score_initial);
6864         putFile8Bit(file, ei->collect_count_initial);
6865
6866         putFile16BitBE(file, ei->push_delay_fixed);
6867         putFile16BitBE(file, ei->push_delay_random);
6868         putFile16BitBE(file, ei->move_delay_fixed);
6869         putFile16BitBE(file, ei->move_delay_random);
6870
6871         putFile16BitBE(file, ei->move_pattern);
6872         putFile8Bit(file, ei->move_direction_initial);
6873         putFile8Bit(file, ei->move_stepsize);
6874
6875         for (y = 0; y < 3; y++)
6876           for (x = 0; x < 3; x++)
6877             putFile16BitBE(file, ei->content.e[x][y]);
6878
6879         putFile32BitBE(file, ei->change->events);
6880
6881         putFile16BitBE(file, ei->change->target_element);
6882
6883         putFile16BitBE(file, ei->change->delay_fixed);
6884         putFile16BitBE(file, ei->change->delay_random);
6885         putFile16BitBE(file, ei->change->delay_frames);
6886
6887         putFile16BitBE(file, ei->change->initial_trigger_element);
6888
6889         putFile8Bit(file, ei->change->explode);
6890         putFile8Bit(file, ei->change->use_target_content);
6891         putFile8Bit(file, ei->change->only_if_complete);
6892         putFile8Bit(file, ei->change->use_random_replace);
6893
6894         putFile8Bit(file, ei->change->random_percentage);
6895         putFile8Bit(file, ei->change->replace_when);
6896
6897         for (y = 0; y < 3; y++)
6898           for (x = 0; x < 3; x++)
6899             putFile16BitBE(file, ei->change->content.e[x][y]);
6900
6901         putFile8Bit(file, ei->slippery_type);
6902
6903         /* some free bytes for future properties and padding */
6904         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
6905       }
6906
6907       check++;
6908     }
6909   }
6910
6911   if (check != num_changed_custom_elements)     /* should not happen */
6912     Error(ERR_WARN, "inconsistent number of custom element properties");
6913 }
6914 #endif
6915
6916 #if ENABLE_HISTORIC_CHUNKS
6917 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
6918 {
6919   struct ElementInfo *ei = &element_info[element];
6920   int i, j, x, y;
6921
6922   /* ---------- custom element base property values (96 bytes) ------------- */
6923
6924   putFile16BitBE(file, element);
6925
6926   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
6927     putFile8Bit(file, ei->description[i]);
6928
6929   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
6930
6931   WriteUnusedBytesToFile(file, 4);      /* reserved for more base properties */
6932
6933   putFile8Bit(file, ei->num_change_pages);
6934
6935   putFile16BitBE(file, ei->ce_value_fixed_initial);
6936   putFile16BitBE(file, ei->ce_value_random_initial);
6937   putFile8Bit(file, ei->use_last_ce_value);
6938
6939   putFile8Bit(file, ei->use_gfx_element);
6940   putFile16BitBE(file, ei->gfx_element_initial);
6941
6942   putFile8Bit(file, ei->collect_score_initial);
6943   putFile8Bit(file, ei->collect_count_initial);
6944
6945   putFile8Bit(file, ei->drop_delay_fixed);
6946   putFile8Bit(file, ei->push_delay_fixed);
6947   putFile8Bit(file, ei->drop_delay_random);
6948   putFile8Bit(file, ei->push_delay_random);
6949   putFile16BitBE(file, ei->move_delay_fixed);
6950   putFile16BitBE(file, ei->move_delay_random);
6951
6952   /* bits 0 - 15 of "move_pattern" ... */
6953   putFile16BitBE(file, ei->move_pattern & 0xffff);
6954   putFile8Bit(file, ei->move_direction_initial);
6955   putFile8Bit(file, ei->move_stepsize);
6956
6957   putFile8Bit(file, ei->slippery_type);
6958
6959   for (y = 0; y < 3; y++)
6960     for (x = 0; x < 3; x++)
6961       putFile16BitBE(file, ei->content.e[x][y]);
6962
6963   putFile16BitBE(file, ei->move_enter_element);
6964   putFile16BitBE(file, ei->move_leave_element);
6965   putFile8Bit(file, ei->move_leave_type);
6966
6967   /* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
6968   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
6969
6970   putFile8Bit(file, ei->access_direction);
6971
6972   putFile8Bit(file, ei->explosion_delay);
6973   putFile8Bit(file, ei->ignition_delay);
6974   putFile8Bit(file, ei->explosion_type);
6975
6976   /* some free bytes for future custom property values and padding */
6977   WriteUnusedBytesToFile(file, 1);
6978
6979   /* ---------- change page property values (48 bytes) --------------------- */
6980
6981   for (i = 0; i < ei->num_change_pages; i++)
6982   {
6983     struct ElementChangeInfo *change = &ei->change_page[i];
6984     unsigned int event_bits;
6985
6986     /* bits 0 - 31 of "has_event[]" ... */
6987     event_bits = 0;
6988     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
6989       if (change->has_event[j])
6990         event_bits |= (1 << j);
6991     putFile32BitBE(file, event_bits);
6992
6993     putFile16BitBE(file, change->target_element);
6994
6995     putFile16BitBE(file, change->delay_fixed);
6996     putFile16BitBE(file, change->delay_random);
6997     putFile16BitBE(file, change->delay_frames);
6998
6999     putFile16BitBE(file, change->initial_trigger_element);
7000
7001     putFile8Bit(file, change->explode);
7002     putFile8Bit(file, change->use_target_content);
7003     putFile8Bit(file, change->only_if_complete);
7004     putFile8Bit(file, change->use_random_replace);
7005
7006     putFile8Bit(file, change->random_percentage);
7007     putFile8Bit(file, change->replace_when);
7008
7009     for (y = 0; y < 3; y++)
7010       for (x = 0; x < 3; x++)
7011         putFile16BitBE(file, change->target_content.e[x][y]);
7012
7013     putFile8Bit(file, change->can_change);
7014
7015     putFile8Bit(file, change->trigger_side);
7016
7017     putFile8Bit(file, change->trigger_player);
7018     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
7019                        log_2(change->trigger_page)));
7020
7021     putFile8Bit(file, change->has_action);
7022     putFile8Bit(file, change->action_type);
7023     putFile8Bit(file, change->action_mode);
7024     putFile16BitBE(file, change->action_arg);
7025
7026     /* ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible) */
7027     event_bits = 0;
7028     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
7029       if (change->has_event[j])
7030         event_bits |= (1 << (j - 32));
7031     putFile8Bit(file, event_bits);
7032   }
7033 }
7034 #endif
7035
7036 #if ENABLE_HISTORIC_CHUNKS
7037 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
7038 {
7039   struct ElementInfo *ei = &element_info[element];
7040   struct ElementGroupInfo *group = ei->group;
7041   int i;
7042
7043   putFile16BitBE(file, element);
7044
7045   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
7046     putFile8Bit(file, ei->description[i]);
7047
7048   putFile8Bit(file, group->num_elements);
7049
7050   putFile8Bit(file, ei->use_gfx_element);
7051   putFile16BitBE(file, ei->gfx_element_initial);
7052
7053   putFile8Bit(file, group->choice_mode);
7054
7055   /* some free bytes for future values and padding */
7056   WriteUnusedBytesToFile(file, 3);
7057
7058   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
7059     putFile16BitBE(file, group->element[i]);
7060 }
7061 #endif
7062
7063 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
7064                                 boolean write_element)
7065 {
7066   int save_type = entry->save_type;
7067   int data_type = entry->data_type;
7068   int conf_type = entry->conf_type;
7069   int byte_mask = conf_type & CONF_MASK_BYTES;
7070   int element = entry->element;
7071   int default_value = entry->default_value;
7072   int num_bytes = 0;
7073   boolean modified = FALSE;
7074
7075   if (byte_mask != CONF_MASK_MULTI_BYTES)
7076   {
7077     void *value_ptr = entry->value;
7078     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
7079                  *(int *)value_ptr);
7080
7081     /* check if any settings have been modified before saving them */
7082     if (value != default_value)
7083       modified = TRUE;
7084
7085     /* do not save if explicitly told or if unmodified default settings */
7086     if ((save_type == SAVE_CONF_NEVER) ||
7087         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7088       return 0;
7089
7090     if (write_element)
7091       num_bytes += putFile16BitBE(file, element);
7092
7093     num_bytes += putFile8Bit(file, conf_type);
7094     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
7095                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
7096                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
7097                   0);
7098   }
7099   else if (data_type == TYPE_STRING)
7100   {
7101     char *default_string = entry->default_string;
7102     char *string = (char *)(entry->value);
7103     int string_length = strlen(string);
7104     int i;
7105
7106     /* check if any settings have been modified before saving them */
7107     if (!strEqual(string, default_string))
7108       modified = TRUE;
7109
7110     /* do not save if explicitly told or if unmodified default settings */
7111     if ((save_type == SAVE_CONF_NEVER) ||
7112         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7113       return 0;
7114
7115     if (write_element)
7116       num_bytes += putFile16BitBE(file, element);
7117
7118     num_bytes += putFile8Bit(file, conf_type);
7119     num_bytes += putFile16BitBE(file, string_length);
7120
7121     for (i = 0; i < string_length; i++)
7122       num_bytes += putFile8Bit(file, string[i]);
7123   }
7124   else if (data_type == TYPE_ELEMENT_LIST)
7125   {
7126     int *element_array = (int *)(entry->value);
7127     int num_elements = *(int *)(entry->num_entities);
7128     int i;
7129
7130     /* check if any settings have been modified before saving them */
7131     for (i = 0; i < num_elements; i++)
7132       if (element_array[i] != default_value)
7133         modified = TRUE;
7134
7135     /* do not save if explicitly told or if unmodified default settings */
7136     if ((save_type == SAVE_CONF_NEVER) ||
7137         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7138       return 0;
7139
7140     if (write_element)
7141       num_bytes += putFile16BitBE(file, element);
7142
7143     num_bytes += putFile8Bit(file, conf_type);
7144     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
7145
7146     for (i = 0; i < num_elements; i++)
7147       num_bytes += putFile16BitBE(file, element_array[i]);
7148   }
7149   else if (data_type == TYPE_CONTENT_LIST)
7150   {
7151     struct Content *content = (struct Content *)(entry->value);
7152     int num_contents = *(int *)(entry->num_entities);
7153     int i, x, y;
7154
7155     /* check if any settings have been modified before saving them */
7156     for (i = 0; i < num_contents; i++)
7157       for (y = 0; y < 3; y++)
7158         for (x = 0; x < 3; x++)
7159           if (content[i].e[x][y] != default_value)
7160             modified = TRUE;
7161
7162     /* do not save if explicitly told or if unmodified default settings */
7163     if ((save_type == SAVE_CONF_NEVER) ||
7164         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
7165       return 0;
7166
7167     if (write_element)
7168       num_bytes += putFile16BitBE(file, element);
7169
7170     num_bytes += putFile8Bit(file, conf_type);
7171     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
7172
7173     for (i = 0; i < num_contents; i++)
7174       for (y = 0; y < 3; y++)
7175         for (x = 0; x < 3; x++)
7176           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
7177   }
7178
7179   return num_bytes;
7180 }
7181
7182 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
7183 {
7184   int chunk_size = 0;
7185   int i;
7186
7187   li = *level;          /* copy level data into temporary buffer */
7188
7189   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
7190     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
7191
7192   return chunk_size;
7193 }
7194
7195 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
7196 {
7197   int chunk_size = 0;
7198   int i;
7199
7200   li = *level;          /* copy level data into temporary buffer */
7201
7202   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
7203     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
7204
7205   return chunk_size;
7206 }
7207
7208 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
7209 {
7210   int envelope_nr = element - EL_ENVELOPE_1;
7211   int chunk_size = 0;
7212   int i;
7213
7214   chunk_size += putFile16BitBE(file, element);
7215
7216   /* copy envelope data into temporary buffer */
7217   xx_envelope = level->envelope[envelope_nr];
7218
7219   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
7220     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
7221
7222   return chunk_size;
7223 }
7224
7225 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
7226 {
7227   struct ElementInfo *ei = &element_info[element];
7228   int chunk_size = 0;
7229   int i, j;
7230
7231   chunk_size += putFile16BitBE(file, element);
7232
7233   xx_ei = *ei;          /* copy element data into temporary buffer */
7234
7235   /* set default description string for this specific element */
7236   strcpy(xx_default_description, getDefaultElementDescription(ei));
7237
7238   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
7239     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
7240
7241   for (i = 0; i < ei->num_change_pages; i++)
7242   {
7243     struct ElementChangeInfo *change = &ei->change_page[i];
7244
7245     xx_current_change_page = i;
7246
7247     xx_change = *change;        /* copy change data into temporary buffer */
7248
7249     resetEventBits();
7250     setEventBitsFromEventFlags(change);
7251
7252     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
7253       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
7254                                          FALSE);
7255   }
7256
7257   return chunk_size;
7258 }
7259
7260 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
7261 {
7262   struct ElementInfo *ei = &element_info[element];
7263   struct ElementGroupInfo *group = ei->group;
7264   int chunk_size = 0;
7265   int i;
7266
7267   chunk_size += putFile16BitBE(file, element);
7268
7269   xx_ei = *ei;          /* copy element data into temporary buffer */
7270   xx_group = *group;    /* copy group data into temporary buffer */
7271
7272   /* set default description string for this specific element */
7273   strcpy(xx_default_description, getDefaultElementDescription(ei));
7274
7275   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
7276     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
7277
7278   return chunk_size;
7279 }
7280
7281 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
7282                                   boolean save_as_template)
7283 {
7284   int chunk_size;
7285   int i;
7286   FILE *file;
7287
7288   if (!(file = fopen(filename, MODE_WRITE)))
7289   {
7290     Error(ERR_WARN, "cannot save level file '%s'", filename);
7291     return;
7292   }
7293
7294   level->file_version = FILE_VERSION_ACTUAL;
7295   level->game_version = GAME_VERSION_ACTUAL;
7296
7297   level->creation_date = getCurrentDate();
7298
7299   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7300   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
7301
7302   chunk_size = SaveLevel_VERS(NULL, level);
7303   putFileChunkBE(file, "VERS", chunk_size);
7304   SaveLevel_VERS(file, level);
7305
7306   chunk_size = SaveLevel_DATE(NULL, level);
7307   putFileChunkBE(file, "DATE", chunk_size);
7308   SaveLevel_DATE(file, level);
7309
7310   chunk_size = SaveLevel_NAME(NULL, level);
7311   putFileChunkBE(file, "NAME", chunk_size);
7312   SaveLevel_NAME(file, level);
7313
7314   chunk_size = SaveLevel_AUTH(NULL, level);
7315   putFileChunkBE(file, "AUTH", chunk_size);
7316   SaveLevel_AUTH(file, level);
7317
7318   chunk_size = SaveLevel_INFO(NULL, level);
7319   putFileChunkBE(file, "INFO", chunk_size);
7320   SaveLevel_INFO(file, level);
7321
7322   chunk_size = SaveLevel_BODY(NULL, level);
7323   putFileChunkBE(file, "BODY", chunk_size);
7324   SaveLevel_BODY(file, level);
7325
7326   chunk_size = SaveLevel_ELEM(NULL, level);
7327   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          /* save if changed */
7328   {
7329     putFileChunkBE(file, "ELEM", chunk_size);
7330     SaveLevel_ELEM(file, level);
7331   }
7332
7333   for (i = 0; i < NUM_ENVELOPES; i++)
7334   {
7335     int element = EL_ENVELOPE_1 + i;
7336
7337     chunk_size = SaveLevel_NOTE(NULL, level, element);
7338     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        /* save if changed */
7339     {
7340       putFileChunkBE(file, "NOTE", chunk_size);
7341       SaveLevel_NOTE(file, level, element);
7342     }
7343   }
7344
7345   /* if not using template level, check for non-default custom/group elements */
7346   if (!level->use_custom_template || save_as_template)
7347   {
7348     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7349     {
7350       int element = EL_CUSTOM_START + i;
7351
7352       chunk_size = SaveLevel_CUSX(NULL, level, element);
7353       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      /* save if changed */
7354       {
7355         putFileChunkBE(file, "CUSX", chunk_size);
7356         SaveLevel_CUSX(file, level, element);
7357       }
7358     }
7359
7360     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
7361     {
7362       int element = EL_GROUP_START + i;
7363
7364       chunk_size = SaveLevel_GRPX(NULL, level, element);
7365       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      /* save if changed */
7366       {
7367         putFileChunkBE(file, "GRPX", chunk_size);
7368         SaveLevel_GRPX(file, level, element);
7369       }
7370     }
7371   }
7372
7373   fclose(file);
7374
7375   SetFilePermissions(filename, PERMS_PRIVATE);
7376 }
7377
7378 void SaveLevel(int nr)
7379 {
7380   char *filename = getDefaultLevelFilename(nr);
7381
7382   SaveLevelFromFilename(&level, filename, FALSE);
7383 }
7384
7385 void SaveLevelTemplate()
7386 {
7387   char *filename = getLocalLevelTemplateFilename();
7388
7389   SaveLevelFromFilename(&level, filename, TRUE);
7390 }
7391
7392 boolean SaveLevelChecked(int nr)
7393 {
7394   char *filename = getDefaultLevelFilename(nr);
7395   boolean new_level = !fileExists(filename);
7396   boolean level_saved = FALSE;
7397
7398   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
7399   {
7400     SaveLevel(nr);
7401
7402     if (new_level)
7403       Request("Level saved!", REQ_CONFIRM);
7404
7405     level_saved = TRUE;
7406   }
7407
7408   return level_saved;
7409 }
7410
7411 void DumpLevel(struct LevelInfo *level)
7412 {
7413   if (level->no_level_file || level->no_valid_file)
7414   {
7415     Error(ERR_WARN, "cannot dump -- no valid level file found");
7416
7417     return;
7418   }
7419
7420   PrintLine("-", 79);
7421   Print("Level xxx (file version %08d, game version %08d)\n",
7422         level->file_version, level->game_version);
7423   PrintLine("-", 79);
7424
7425   Print("Level author: '%s'\n", level->author);
7426   Print("Level title:  '%s'\n", level->name);
7427   Print("\n");
7428   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
7429   Print("\n");
7430   Print("Level time:  %d seconds\n", level->time);
7431   Print("Gems needed: %d\n", level->gems_needed);
7432   Print("\n");
7433   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
7434   Print("Time for wheel:      %d seconds\n", level->time_wheel);
7435   Print("Time for light:      %d seconds\n", level->time_light);
7436   Print("Time for timegate:   %d seconds\n", level->time_timegate);
7437   Print("\n");
7438   Print("Amoeba speed: %d\n", level->amoeba_speed);
7439   Print("\n");
7440
7441   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
7442   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
7443   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
7444   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
7445   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
7446
7447   PrintLine("-", 79);
7448 }
7449
7450
7451 /* ========================================================================= */
7452 /* tape file functions                                                       */
7453 /* ========================================================================= */
7454
7455 static void setTapeInfoToDefaults()
7456 {
7457   int i;
7458
7459   /* always start with reliable default values (empty tape) */
7460   TapeErase();
7461
7462   /* default values (also for pre-1.2 tapes) with only the first player */
7463   tape.player_participates[0] = TRUE;
7464   for (i = 1; i < MAX_PLAYERS; i++)
7465     tape.player_participates[i] = FALSE;
7466
7467   /* at least one (default: the first) player participates in every tape */
7468   tape.num_participating_players = 1;
7469
7470   tape.level_nr = level_nr;
7471   tape.counter = 0;
7472   tape.changed = FALSE;
7473
7474   tape.recording = FALSE;
7475   tape.playing = FALSE;
7476   tape.pausing = FALSE;
7477
7478   tape.no_valid_file = FALSE;
7479 }
7480
7481 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
7482 {
7483   tape->file_version = getFileVersion(file);
7484   tape->game_version = getFileVersion(file);
7485
7486   return chunk_size;
7487 }
7488
7489 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
7490 {
7491   int i;
7492
7493   tape->random_seed = getFile32BitBE(file);
7494   tape->date        = getFile32BitBE(file);
7495   tape->length      = getFile32BitBE(file);
7496
7497   /* read header fields that are new since version 1.2 */
7498   if (tape->file_version >= FILE_VERSION_1_2)
7499   {
7500     byte store_participating_players = getFile8Bit(file);
7501     int engine_version;
7502
7503     /* since version 1.2, tapes store which players participate in the tape */
7504     tape->num_participating_players = 0;
7505     for (i = 0; i < MAX_PLAYERS; i++)
7506     {
7507       tape->player_participates[i] = FALSE;
7508
7509       if (store_participating_players & (1 << i))
7510       {
7511         tape->player_participates[i] = TRUE;
7512         tape->num_participating_players++;
7513       }
7514     }
7515
7516     ReadUnusedBytesFromFile(file, TAPE_CHUNK_HEAD_UNUSED);
7517
7518     engine_version = getFileVersion(file);
7519     if (engine_version > 0)
7520       tape->engine_version = engine_version;
7521     else
7522       tape->engine_version = tape->game_version;
7523   }
7524
7525   return chunk_size;
7526 }
7527
7528 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
7529 {
7530   int level_identifier_size;
7531   int i;
7532
7533   level_identifier_size = getFile16BitBE(file);
7534
7535   tape->level_identifier =
7536     checked_realloc(tape->level_identifier, level_identifier_size);
7537
7538   for (i = 0; i < level_identifier_size; i++)
7539     tape->level_identifier[i] = getFile8Bit(file);
7540
7541   tape->level_nr = getFile16BitBE(file);
7542
7543   chunk_size = 2 + level_identifier_size + 2;
7544
7545   return chunk_size;
7546 }
7547
7548 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
7549 {
7550   int i, j;
7551   int chunk_size_expected =
7552     (tape->num_participating_players + 1) * tape->length;
7553
7554   if (chunk_size_expected != chunk_size)
7555   {
7556     ReadUnusedBytesFromFile(file, chunk_size);
7557     return chunk_size_expected;
7558   }
7559
7560   for (i = 0; i < tape->length; i++)
7561   {
7562     if (i >= MAX_TAPE_LEN)
7563     {
7564       Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
7565             MAX_TAPE_LEN);
7566
7567       // tape too large; read and ignore remaining tape data from this chunk
7568       for (;i < tape->length; i++)
7569         ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
7570
7571       break;
7572     }
7573
7574     for (j = 0; j < MAX_PLAYERS; j++)
7575     {
7576       tape->pos[i].action[j] = MV_NONE;
7577
7578       if (tape->player_participates[j])
7579         tape->pos[i].action[j] = getFile8Bit(file);
7580     }
7581
7582     tape->pos[i].delay = getFile8Bit(file);
7583
7584     if (tape->file_version == FILE_VERSION_1_0)
7585     {
7586       /* eliminate possible diagonal moves in old tapes */
7587       /* this is only for backward compatibility */
7588
7589       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
7590       byte action = tape->pos[i].action[0];
7591       int k, num_moves = 0;
7592
7593       for (k = 0; k<4; k++)
7594       {
7595         if (action & joy_dir[k])
7596         {
7597           tape->pos[i + num_moves].action[0] = joy_dir[k];
7598           if (num_moves > 0)
7599             tape->pos[i + num_moves].delay = 0;
7600           num_moves++;
7601         }
7602       }
7603
7604       if (num_moves > 1)
7605       {
7606         num_moves--;
7607         i += num_moves;
7608         tape->length += num_moves;
7609       }
7610     }
7611     else if (tape->file_version < FILE_VERSION_2_0)
7612     {
7613       /* convert pre-2.0 tapes to new tape format */
7614
7615       if (tape->pos[i].delay > 1)
7616       {
7617         /* action part */
7618         tape->pos[i + 1] = tape->pos[i];
7619         tape->pos[i + 1].delay = 1;
7620
7621         /* delay part */
7622         for (j = 0; j < MAX_PLAYERS; j++)
7623           tape->pos[i].action[j] = MV_NONE;
7624         tape->pos[i].delay--;
7625
7626         i++;
7627         tape->length++;
7628       }
7629     }
7630
7631     if (checkEndOfFile(file))
7632       break;
7633   }
7634
7635   if (i != tape->length)
7636     chunk_size = (tape->num_participating_players + 1) * i;
7637
7638   return chunk_size;
7639 }
7640
7641 void LoadTape_SokobanSolution(char *filename)
7642 {
7643   File *file;
7644   int move_delay = TILESIZE / level.initial_player_stepsize[0];
7645
7646   if (!(file = openFile(filename, MODE_READ)))
7647   {
7648     tape.no_valid_file = TRUE;
7649
7650     return;
7651   }
7652
7653   while (!checkEndOfFile(file))
7654   {
7655     unsigned char c = getByteFromFile(file);
7656
7657     if (checkEndOfFile(file))
7658       break;
7659
7660     switch (c)
7661     {
7662       case 'u':
7663       case 'U':
7664         tape.pos[tape.length].action[0] = MV_UP;
7665         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7666         tape.length++;
7667         break;
7668
7669       case 'd':
7670       case 'D':
7671         tape.pos[tape.length].action[0] = MV_DOWN;
7672         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7673         tape.length++;
7674         break;
7675
7676       case 'l':
7677       case 'L':
7678         tape.pos[tape.length].action[0] = MV_LEFT;
7679         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7680         tape.length++;
7681         break;
7682
7683       case 'r':
7684       case 'R':
7685         tape.pos[tape.length].action[0] = MV_RIGHT;
7686         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
7687         tape.length++;
7688         break;
7689
7690       case '\n':
7691       case '\r':
7692       case '\t':
7693       case ' ':
7694         /* ignore white-space characters */
7695         break;
7696
7697       default:
7698         tape.no_valid_file = TRUE;
7699
7700         Error(ERR_WARN, "unsupported Sokoban solution file '%s' ['%d']", filename, c);
7701
7702         break;
7703     }
7704   }
7705
7706   closeFile(file);
7707
7708   if (tape.no_valid_file)
7709     return;
7710
7711   tape.length_frames  = GetTapeLengthFrames();
7712   tape.length_seconds = GetTapeLengthSeconds();
7713 }
7714
7715 void LoadTapeFromFilename(char *filename)
7716 {
7717   char cookie[MAX_LINE_LEN];
7718   char chunk_name[CHUNK_ID_LEN + 1];
7719   File *file;
7720   int chunk_size;
7721
7722   /* always start with reliable default values */
7723   setTapeInfoToDefaults();
7724
7725   if (strSuffix(filename, ".sln"))
7726   {
7727     LoadTape_SokobanSolution(filename);
7728
7729     return;
7730   }
7731
7732   if (!(file = openFile(filename, MODE_READ)))
7733   {
7734     tape.no_valid_file = TRUE;
7735
7736     return;
7737   }
7738
7739   getFileChunkBE(file, chunk_name, NULL);
7740   if (strEqual(chunk_name, "RND1"))
7741   {
7742     getFile32BitBE(file);               /* not used */
7743
7744     getFileChunkBE(file, chunk_name, NULL);
7745     if (!strEqual(chunk_name, "TAPE"))
7746     {
7747       tape.no_valid_file = TRUE;
7748
7749       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7750
7751       closeFile(file);
7752
7753       return;
7754     }
7755   }
7756   else  /* check for pre-2.0 file format with cookie string */
7757   {
7758     strcpy(cookie, chunk_name);
7759     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
7760       cookie[4] = '\0';
7761     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
7762       cookie[strlen(cookie) - 1] = '\0';
7763
7764     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
7765     {
7766       tape.no_valid_file = TRUE;
7767
7768       Error(ERR_WARN, "unknown format of tape file '%s'", filename);
7769
7770       closeFile(file);
7771
7772       return;
7773     }
7774
7775     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
7776     {
7777       tape.no_valid_file = TRUE;
7778
7779       Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
7780
7781       closeFile(file);
7782
7783       return;
7784     }
7785
7786     /* pre-2.0 tape files have no game version, so use file version here */
7787     tape.game_version = tape.file_version;
7788   }
7789
7790   if (tape.file_version < FILE_VERSION_1_2)
7791   {
7792     /* tape files from versions before 1.2.0 without chunk structure */
7793     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
7794     LoadTape_BODY(file, 2 * tape.length,      &tape);
7795   }
7796   else
7797   {
7798     static struct
7799     {
7800       char *name;
7801       int size;
7802       int (*loader)(File *, int, struct TapeInfo *);
7803     }
7804     chunk_info[] =
7805     {
7806       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
7807       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
7808       { "INFO", -1,                     LoadTape_INFO },
7809       { "BODY", -1,                     LoadTape_BODY },
7810       {  NULL,  0,                      NULL }
7811     };
7812
7813     while (getFileChunkBE(file, chunk_name, &chunk_size))
7814     {
7815       int i = 0;
7816
7817       while (chunk_info[i].name != NULL &&
7818              !strEqual(chunk_name, chunk_info[i].name))
7819         i++;
7820
7821       if (chunk_info[i].name == NULL)
7822       {
7823         Error(ERR_WARN, "unknown chunk '%s' in tape file '%s'",
7824               chunk_name, filename);
7825         ReadUnusedBytesFromFile(file, chunk_size);
7826       }
7827       else if (chunk_info[i].size != -1 &&
7828                chunk_info[i].size != chunk_size)
7829       {
7830         Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7831               chunk_size, chunk_name, filename);
7832         ReadUnusedBytesFromFile(file, chunk_size);
7833       }
7834       else
7835       {
7836         /* call function to load this tape chunk */
7837         int chunk_size_expected =
7838           (chunk_info[i].loader)(file, chunk_size, &tape);
7839
7840         /* the size of some chunks cannot be checked before reading other
7841            chunks first (like "HEAD" and "BODY") that contain some header
7842            information, so check them here */
7843         if (chunk_size_expected != chunk_size)
7844         {
7845           Error(ERR_WARN, "wrong size (%d) of chunk '%s' in tape file '%s'",
7846                 chunk_size, chunk_name, filename);
7847         }
7848       }
7849     }
7850   }
7851
7852   closeFile(file);
7853
7854   tape.length_frames  = GetTapeLengthFrames();
7855   tape.length_seconds = GetTapeLengthSeconds();
7856
7857 #if 0
7858   printf("::: tape file version: %d\n",   tape.file_version);
7859   printf("::: tape game version: %d\n",   tape.game_version);
7860   printf("::: tape engine version: %d\n", tape.engine_version);
7861 #endif
7862 }
7863
7864 void LoadTape(int nr)
7865 {
7866   char *filename = getTapeFilename(nr);
7867
7868   LoadTapeFromFilename(filename);
7869 }
7870
7871 void LoadSolutionTape(int nr)
7872 {
7873   char *filename = getSolutionTapeFilename(nr);
7874
7875   LoadTapeFromFilename(filename);
7876
7877   if (TAPE_IS_EMPTY(tape) &&
7878       level.game_engine_type == GAME_ENGINE_TYPE_SP &&
7879       level.native_sp_level->demo.is_available)
7880     CopyNativeTape_SP_to_RND(&level);
7881 }
7882
7883 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
7884 {
7885   putFileVersion(file, tape->file_version);
7886   putFileVersion(file, tape->game_version);
7887 }
7888
7889 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
7890 {
7891   int i;
7892   byte store_participating_players = 0;
7893
7894   /* set bits for participating players for compact storage */
7895   for (i = 0; i < MAX_PLAYERS; i++)
7896     if (tape->player_participates[i])
7897       store_participating_players |= (1 << i);
7898
7899   putFile32BitBE(file, tape->random_seed);
7900   putFile32BitBE(file, tape->date);
7901   putFile32BitBE(file, tape->length);
7902
7903   putFile8Bit(file, store_participating_players);
7904
7905   /* unused bytes not at the end here for 4-byte alignment of engine_version */
7906   WriteUnusedBytesToFile(file, TAPE_CHUNK_HEAD_UNUSED);
7907
7908   putFileVersion(file, tape->engine_version);
7909 }
7910
7911 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
7912 {
7913   int level_identifier_size = strlen(tape->level_identifier) + 1;
7914   int i;
7915
7916   putFile16BitBE(file, level_identifier_size);
7917
7918   for (i = 0; i < level_identifier_size; i++)
7919     putFile8Bit(file, tape->level_identifier[i]);
7920
7921   putFile16BitBE(file, tape->level_nr);
7922 }
7923
7924 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
7925 {
7926   int i, j;
7927
7928   for (i = 0; i < tape->length; i++)
7929   {
7930     for (j = 0; j < MAX_PLAYERS; j++)
7931       if (tape->player_participates[j])
7932         putFile8Bit(file, tape->pos[i].action[j]);
7933
7934     putFile8Bit(file, tape->pos[i].delay);
7935   }
7936 }
7937
7938 void SaveTape(int nr)
7939 {
7940   char *filename = getTapeFilename(nr);
7941   FILE *file;
7942   int num_participating_players = 0;
7943   int info_chunk_size;
7944   int body_chunk_size;
7945   int i;
7946
7947   InitTapeDirectory(leveldir_current->subdir);
7948
7949   if (!(file = fopen(filename, MODE_WRITE)))
7950   {
7951     Error(ERR_WARN, "cannot save level recording file '%s'", filename);
7952     return;
7953   }
7954
7955   tape.file_version = FILE_VERSION_ACTUAL;
7956   tape.game_version = GAME_VERSION_ACTUAL;
7957
7958   /* count number of participating players  */
7959   for (i = 0; i < MAX_PLAYERS; i++)
7960     if (tape.player_participates[i])
7961       num_participating_players++;
7962
7963   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
7964   body_chunk_size = (num_participating_players + 1) * tape.length;
7965
7966   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
7967   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
7968
7969   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
7970   SaveTape_VERS(file, &tape);
7971
7972   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
7973   SaveTape_HEAD(file, &tape);
7974
7975   putFileChunkBE(file, "INFO", info_chunk_size);
7976   SaveTape_INFO(file, &tape);
7977
7978   putFileChunkBE(file, "BODY", body_chunk_size);
7979   SaveTape_BODY(file, &tape);
7980
7981   fclose(file);
7982
7983   SetFilePermissions(filename, PERMS_PRIVATE);
7984
7985   tape.changed = FALSE;
7986 }
7987
7988 boolean SaveTapeChecked(int nr)
7989 {
7990   char *filename = getTapeFilename(nr);
7991   boolean new_tape = !fileExists(filename);
7992   boolean tape_saved = FALSE;
7993
7994   if (new_tape || Request("Replace old tape?", REQ_ASK))
7995   {
7996     SaveTape(nr);
7997
7998     if (new_tape)
7999       Request("Tape saved!", REQ_CONFIRM);
8000
8001     tape_saved = TRUE;
8002   }
8003
8004   return tape_saved;
8005 }
8006
8007 void DumpTape(struct TapeInfo *tape)
8008 {
8009   int tape_frame_counter;
8010   int i, j;
8011
8012   if (tape->no_valid_file)
8013   {
8014     Error(ERR_WARN, "cannot dump -- no valid tape file found");
8015
8016     return;
8017   }
8018
8019   PrintLine("-", 79);
8020   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
8021         tape->level_nr, tape->file_version, tape->game_version);
8022   Print("                  (effective engine version %08d)\n",
8023         tape->engine_version);
8024   Print("Level series identifier: '%s'\n", tape->level_identifier);
8025   PrintLine("-", 79);
8026
8027   tape_frame_counter = 0;
8028
8029   for (i = 0; i < tape->length; i++)
8030   {
8031     if (i >= MAX_TAPE_LEN)
8032       break;
8033
8034     Print("%04d: ", i);
8035
8036     for (j = 0; j < MAX_PLAYERS; j++)
8037     {
8038       if (tape->player_participates[j])
8039       {
8040         int action = tape->pos[i].action[j];
8041
8042         Print("%d:%02x ", j, action);
8043         Print("[%c%c%c%c|%c%c] - ",
8044               (action & JOY_LEFT ? '<' : ' '),
8045               (action & JOY_RIGHT ? '>' : ' '),
8046               (action & JOY_UP ? '^' : ' '),
8047               (action & JOY_DOWN ? 'v' : ' '),
8048               (action & JOY_BUTTON_1 ? '1' : ' '),
8049               (action & JOY_BUTTON_2 ? '2' : ' '));
8050       }
8051     }
8052
8053     Print("(%03d) ", tape->pos[i].delay);
8054     Print("[%05d]\n", tape_frame_counter);
8055
8056     tape_frame_counter += tape->pos[i].delay;
8057   }
8058
8059   PrintLine("-", 79);
8060 }
8061
8062
8063 /* ========================================================================= */
8064 /* score file functions                                                      */
8065 /* ========================================================================= */
8066
8067 void LoadScore(int nr)
8068 {
8069   int i;
8070   char *filename = getScoreFilename(nr);
8071   char cookie[MAX_LINE_LEN];
8072   char line[MAX_LINE_LEN];
8073   char *line_ptr;
8074   FILE *file;
8075
8076   /* always start with reliable default values */
8077   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8078   {
8079     strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
8080     highscore[i].Score = 0;
8081   }
8082
8083   if (!(file = fopen(filename, MODE_READ)))
8084     return;
8085
8086   /* check file identifier */
8087   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
8088     cookie[0] = '\0';
8089   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
8090     cookie[strlen(cookie) - 1] = '\0';
8091
8092   if (!checkCookieString(cookie, SCORE_COOKIE))
8093   {
8094     Error(ERR_WARN, "unknown format of score file '%s'", filename);
8095     fclose(file);
8096     return;
8097   }
8098
8099   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8100   {
8101     if (fscanf(file, "%d", &highscore[i].Score) == EOF)
8102       Error(ERR_WARN, "fscanf() failed; %s", strerror(errno));
8103     if (fgets(line, MAX_LINE_LEN, file) == NULL)
8104       line[0] = '\0';
8105
8106     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
8107       line[strlen(line) - 1] = '\0';
8108
8109     for (line_ptr = line; *line_ptr; line_ptr++)
8110     {
8111       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
8112       {
8113         strncpy(highscore[i].Name, line_ptr, MAX_PLAYER_NAME_LEN);
8114         highscore[i].Name[MAX_PLAYER_NAME_LEN] = '\0';
8115         break;
8116       }
8117     }
8118   }
8119
8120   fclose(file);
8121 }
8122
8123 void SaveScore(int nr)
8124 {
8125   int i;
8126   int permissions = (program.global_scores ? PERMS_PUBLIC : PERMS_PRIVATE);
8127   char *filename = getScoreFilename(nr);
8128   FILE *file;
8129
8130   InitScoreDirectory(leveldir_current->subdir);
8131
8132   if (!(file = fopen(filename, MODE_WRITE)))
8133   {
8134     Error(ERR_WARN, "cannot save score for level %d", nr);
8135     return;
8136   }
8137
8138   fprintf(file, "%s\n\n", SCORE_COOKIE);
8139
8140   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
8141     fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
8142
8143   fclose(file);
8144
8145   SetFilePermissions(filename, permissions);
8146 }
8147
8148
8149 /* ========================================================================= */
8150 /* setup file functions                                                      */
8151 /* ========================================================================= */
8152
8153 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
8154
8155 /* global setup */
8156 #define SETUP_TOKEN_PLAYER_NAME                 0
8157 #define SETUP_TOKEN_SOUND                       1
8158 #define SETUP_TOKEN_SOUND_LOOPS                 2
8159 #define SETUP_TOKEN_SOUND_MUSIC                 3
8160 #define SETUP_TOKEN_SOUND_SIMPLE                4
8161 #define SETUP_TOKEN_TOONS                       5
8162 #define SETUP_TOKEN_SCROLL_DELAY                6
8163 #define SETUP_TOKEN_SCROLL_DELAY_VALUE          7
8164 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MODE        8
8165 #define SETUP_TOKEN_ENGINE_SNAPSHOT_MEMORY      9
8166 #define SETUP_TOKEN_FADE_SCREENS                10
8167 #define SETUP_TOKEN_AUTORECORD                  11
8168 #define SETUP_TOKEN_SHOW_TITLESCREEN            12
8169 #define SETUP_TOKEN_QUICK_DOORS                 13
8170 #define SETUP_TOKEN_TEAM_MODE                   14
8171 #define SETUP_TOKEN_HANDICAP                    15
8172 #define SETUP_TOKEN_SKIP_LEVELS                 16
8173 #define SETUP_TOKEN_INCREMENT_LEVELS            17
8174 #define SETUP_TOKEN_TIME_LIMIT                  18
8175 #define SETUP_TOKEN_FULLSCREEN                  19
8176 #define SETUP_TOKEN_WINDOW_SCALING_PERCENT      20
8177 #define SETUP_TOKEN_WINDOW_SCALING_QUALITY      21
8178 #define SETUP_TOKEN_SCREEN_RENDERING_MODE       22
8179 #define SETUP_TOKEN_ASK_ON_ESCAPE               23
8180 #define SETUP_TOKEN_ASK_ON_ESCAPE_EDITOR        24
8181 #define SETUP_TOKEN_QUICK_SWITCH                25
8182 #define SETUP_TOKEN_INPUT_ON_FOCUS              26
8183 #define SETUP_TOKEN_PREFER_AGA_GRAPHICS         27
8184 #define SETUP_TOKEN_GAME_FRAME_DELAY            28
8185 #define SETUP_TOKEN_SP_SHOW_BORDER_ELEMENTS     29
8186 #define SETUP_TOKEN_SMALL_GAME_GRAPHICS         30
8187 #define SETUP_TOKEN_SHOW_SNAPSHOT_BUTTONS       31
8188 #define SETUP_TOKEN_GRAPHICS_SET                32
8189 #define SETUP_TOKEN_SOUNDS_SET                  33
8190 #define SETUP_TOKEN_MUSIC_SET                   34
8191 #define SETUP_TOKEN_OVERRIDE_LEVEL_GRAPHICS     35
8192 #define SETUP_TOKEN_OVERRIDE_LEVEL_SOUNDS       36
8193 #define SETUP_TOKEN_OVERRIDE_LEVEL_MUSIC        37
8194 #define SETUP_TOKEN_VOLUME_SIMPLE               38
8195 #define SETUP_TOKEN_VOLUME_LOOPS                39
8196 #define SETUP_TOKEN_VOLUME_MUSIC                40
8197 #define SETUP_TOKEN_TOUCH_CONTROL_TYPE          41
8198 #define SETUP_TOKEN_TOUCH_MOVE_DISTANCE         42
8199 #define SETUP_TOKEN_TOUCH_DROP_DISTANCE         43
8200
8201 #define NUM_GLOBAL_SETUP_TOKENS                 44
8202
8203 /* editor setup */
8204 #define SETUP_TOKEN_EDITOR_EL_CLASSIC           0
8205 #define SETUP_TOKEN_EDITOR_EL_CUSTOM            1
8206 #define SETUP_TOKEN_EDITOR_EL_USER_DEFINED      2
8207 #define SETUP_TOKEN_EDITOR_EL_DYNAMIC           3
8208 #define SETUP_TOKEN_EDITOR_EL_HEADLINES         4
8209 #define SETUP_TOKEN_EDITOR_SHOW_ELEMENT_TOKEN   5
8210
8211 #define NUM_EDITOR_SETUP_TOKENS                 6
8212
8213 /* editor cascade setup */
8214 #define SETUP_TOKEN_EDITOR_CASCADE_BD           0
8215 #define SETUP_TOKEN_EDITOR_CASCADE_EM           1
8216 #define SETUP_TOKEN_EDITOR_CASCADE_EMC          2
8217 #define SETUP_TOKEN_EDITOR_CASCADE_RND          3
8218 #define SETUP_TOKEN_EDITOR_CASCADE_SB           4
8219 #define SETUP_TOKEN_EDITOR_CASCADE_SP           5
8220 #define SETUP_TOKEN_EDITOR_CASCADE_DC           6
8221 #define SETUP_TOKEN_EDITOR_CASCADE_DX           7
8222 #define SETUP_TOKEN_EDITOR_CASCADE_TEXT         8
8223 #define SETUP_TOKEN_EDITOR_CASCADE_STEELTEXT    9
8224 #define SETUP_TOKEN_EDITOR_CASCADE_CE           10
8225 #define SETUP_TOKEN_EDITOR_CASCADE_GE           11
8226 #define SETUP_TOKEN_EDITOR_CASCADE_REF          12
8227 #define SETUP_TOKEN_EDITOR_CASCADE_USER         13
8228 #define SETUP_TOKEN_EDITOR_CASCADE_DYNAMIC      14
8229
8230 #define NUM_EDITOR_CASCADE_SETUP_TOKENS         15
8231
8232 /* shortcut setup */
8233 #define SETUP_TOKEN_SHORTCUT_SAVE_GAME          0
8234 #define SETUP_TOKEN_SHORTCUT_LOAD_GAME          1
8235 #define SETUP_TOKEN_SHORTCUT_TOGGLE_PAUSE       2
8236 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_1     3
8237 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_2     4
8238 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_3     5
8239 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_4     6
8240 #define SETUP_TOKEN_SHORTCUT_FOCUS_PLAYER_ALL   7
8241 #define SETUP_TOKEN_SHORTCUT_TAPE_EJECT         8
8242 #define SETUP_TOKEN_SHORTCUT_TAPE_EXTRA         9
8243 #define SETUP_TOKEN_SHORTCUT_TAPE_STOP          10
8244 #define SETUP_TOKEN_SHORTCUT_TAPE_PAUSE         11
8245 #define SETUP_TOKEN_SHORTCUT_TAPE_RECORD        12
8246 #define SETUP_TOKEN_SHORTCUT_TAPE_PLAY          13
8247 #define SETUP_TOKEN_SHORTCUT_SOUND_SIMPLE       14
8248 #define SETUP_TOKEN_SHORTCUT_SOUND_LOOPS        15
8249 #define SETUP_TOKEN_SHORTCUT_SOUND_MUSIC        16
8250 #define SETUP_TOKEN_SHORTCUT_SNAP_LEFT          17
8251 #define SETUP_TOKEN_SHORTCUT_SNAP_RIGHT         18
8252 #define SETUP_TOKEN_SHORTCUT_SNAP_UP            19
8253 #define SETUP_TOKEN_SHORTCUT_SNAP_DOWN          20
8254
8255 #define NUM_SHORTCUT_SETUP_TOKENS               21
8256
8257 /* player setup */
8258 #define SETUP_TOKEN_PLAYER_USE_JOYSTICK         0
8259 #define SETUP_TOKEN_PLAYER_JOY_DEVICE_NAME      1
8260 #define SETUP_TOKEN_PLAYER_JOY_XLEFT            2
8261 #define SETUP_TOKEN_PLAYER_JOY_XMIDDLE          3
8262 #define SETUP_TOKEN_PLAYER_JOY_XRIGHT           4
8263 #define SETUP_TOKEN_PLAYER_JOY_YUPPER           5
8264 #define SETUP_TOKEN_PLAYER_JOY_YMIDDLE          6
8265 #define SETUP_TOKEN_PLAYER_JOY_YLOWER           7
8266 #define SETUP_TOKEN_PLAYER_JOY_SNAP             8
8267 #define SETUP_TOKEN_PLAYER_JOY_DROP             9
8268 #define SETUP_TOKEN_PLAYER_KEY_LEFT             10
8269 #define SETUP_TOKEN_PLAYER_KEY_RIGHT            11
8270 #define SETUP_TOKEN_PLAYER_KEY_UP               12
8271 #define SETUP_TOKEN_PLAYER_KEY_DOWN             13
8272 #define SETUP_TOKEN_PLAYER_KEY_SNAP             14
8273 #define SETUP_TOKEN_PLAYER_KEY_DROP             15
8274
8275 #define NUM_PLAYER_SETUP_TOKENS                 16
8276
8277 /* system setup */
8278 #define SETUP_TOKEN_SYSTEM_SDL_VIDEODRIVER      0
8279 #define SETUP_TOKEN_SYSTEM_SDL_AUDIODRIVER      1
8280 #define SETUP_TOKEN_SYSTEM_AUDIO_FRAGMENT_SIZE  2
8281
8282 #define NUM_SYSTEM_SETUP_TOKENS                 3
8283
8284 /* internal setup */
8285 #define SETUP_TOKEN_INT_PROGRAM_TITLE           0
8286 #define SETUP_TOKEN_INT_PROGRAM_VERSION         1
8287 #define SETUP_TOKEN_INT_PROGRAM_AUTHOR          2
8288 #define SETUP_TOKEN_INT_PROGRAM_EMAIL           3
8289 #define SETUP_TOKEN_INT_PROGRAM_WEBSITE         4
8290 #define SETUP_TOKEN_INT_PROGRAM_COPYRIGHT       5
8291 #define SETUP_TOKEN_INT_PROGRAM_COMPANY         6
8292 #define SETUP_TOKEN_INT_PROGRAM_ICON_FILE       7
8293 #define SETUP_TOKEN_INT_DEFAULT_GRAPHICS_SET    8
8294 #define SETUP_TOKEN_INT_DEFAULT_SOUNDS_SET      9
8295 #define SETUP_TOKEN_INT_DEFAULT_MUSIC_SET       10
8296 #define SETUP_TOKEN_INT_FALLBACK_GRAPHICS_FILE  11
8297 #define SETUP_TOKEN_INT_FALLBACK_SOUNDS_FILE    12
8298 #define SETUP_TOKEN_INT_FALLBACK_MUSIC_FILE     13
8299 #define SETUP_TOKEN_INT_DEFAULT_LEVEL_SERIES    14
8300 #define SETUP_TOKEN_INT_CHOOSE_FROM_TOP_LEVELDIR 15
8301 #define SETUP_TOKEN_INT_SHOW_SCALING_IN_TITLE   16
8302 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_WIDTH    17
8303 #define SETUP_TOKEN_INT_DEFAULT_WINDOW_HEIGHT   18
8304
8305 #define NUM_INTERNAL_SETUP_TOKENS               19
8306
8307 /* debug setup */
8308 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_0         0
8309 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_1         1
8310 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_2         2
8311 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_3         3
8312 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_4         4
8313 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_5         5
8314 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_6         6
8315 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_7         7
8316 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_8         8
8317 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_9         9
8318 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_0     10
8319 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_1     11
8320 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_2     12
8321 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_3     13
8322 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_4     14
8323 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_5     15
8324 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_6     16
8325 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_7     17
8326 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_8     18
8327 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_KEY_9     19
8328 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_USE_MOD_KEY 20
8329 #define SETUP_TOKEN_DEBUG_FRAME_DELAY_GAME_ONLY 21
8330 #define SETUP_TOKEN_DEBUG_SHOW_FRAMES_PER_SECOND 22
8331
8332 #define NUM_DEBUG_SETUP_TOKENS                  23
8333
8334 /* options setup */
8335 #define SETUP_TOKEN_OPTIONS_VERBOSE             0
8336
8337 #define NUM_OPTIONS_SETUP_TOKENS                1
8338
8339
8340 static struct SetupInfo si;
8341 static struct SetupEditorInfo sei;
8342 static struct SetupEditorCascadeInfo seci;
8343 static struct SetupShortcutInfo ssi;
8344 static struct SetupInputInfo sii;
8345 static struct SetupSystemInfo syi;
8346 static struct SetupInternalInfo sxi;
8347 static struct SetupDebugInfo sdi;
8348 static struct OptionInfo soi;
8349
8350 static struct TokenInfo global_setup_tokens[] =
8351 {
8352   { TYPE_STRING, &si.player_name,             "player_name"             },
8353   { TYPE_SWITCH, &si.sound,                   "sound"                   },
8354   { TYPE_SWITCH, &si.sound_loops,             "repeating_sound_loops"   },
8355   { TYPE_SWITCH, &si.sound_music,             "background_music"        },
8356   { TYPE_SWITCH, &si.sound_simple,            "simple_sound_effects"    },
8357   { TYPE_SWITCH, &si.toons,                   "toons"                   },
8358   { TYPE_SWITCH, &si.scroll_delay,            "scroll_delay"            },
8359   { TYPE_INTEGER,&si.scroll_delay_value,      "scroll_delay_value"      },
8360   { TYPE_STRING, &si.engine_snapshot_mode,    "engine_snapshot_mode"    },
8361   { TYPE_INTEGER,&si.engine_snapshot_memory,  "engine_snapshot_memory"  },
8362   { TYPE_SWITCH, &si.fade_screens,            "fade_screens"            },
8363   { TYPE_SWITCH, &si.autorecord,              "automatic_tape_recording"},
8364   { TYPE_SWITCH, &si.show_titlescreen,        "show_titlescreen"        },
8365   { TYPE_SWITCH, &si.quick_doors,             "quick_doors"             },
8366   { TYPE_SWITCH, &si.team_mode,               "team_mode"               },
8367   { TYPE_SWITCH, &si.handicap,                "handicap"                },
8368   { TYPE_SWITCH, &si.skip_levels,             "skip_levels"             },
8369   { TYPE_SWITCH, &si.increment_levels,        "increment_levels"        },
8370   { TYPE_SWITCH, &si.time_limit,              "time_limit"              },
8371   { TYPE_SWITCH, &si.fullscreen,              "fullscreen"              },
8372   { TYPE_INTEGER,&si.window_scaling_percent,  "window_scaling_percent"  },
8373   { TYPE_STRING, &si.window_scaling_quality,  "window_scaling_quality"  },
8374   { TYPE_STRING, &si.screen_rendering_mode,   "screen_rendering_mode"   },
8375   { TYPE_SWITCH, &si.ask_on_escape,           "ask_on_escape"           },
8376   { TYPE_SWITCH, &si.ask_on_escape_editor,    "ask_on_escape_editor"    },
8377   { TYPE_SWITCH, &si.quick_switch,            "quick_player_switch"     },
8378   { TYPE_SWITCH, &si.input_on_focus,          "input_on_focus"          },
8379   { TYPE_SWITCH, &si.prefer_aga_graphics,     "prefer_aga_graphics"     },
8380   { TYPE_INTEGER,&si.game_frame_delay,        "game_frame_delay"        },
8381   { TYPE_SWITCH, &si.sp_show_border_elements, "sp_show_border_elements" },
8382   { TYPE_SWITCH, &si.small_game_graphics,     "small_game_graphics"     },
8383   { TYPE_SWITCH, &si.show_snapshot_buttons,   "show_snapshot_buttons"   },
8384   { TYPE_STRING, &si.graphics_set,            "graphics_set"            },
8385   { TYPE_STRING, &si.sounds_set,              "sounds_set"              },
8386   { TYPE_STRING, &si.music_set,               "music_set"               },
8387   { TYPE_SWITCH3,&si.override_level_graphics, "override_level_graphics" },
8388   { TYPE_SWITCH3,&si.override_level_sounds,   "override_level_sounds"   },
8389   { TYPE_SWITCH3,&si.override_level_music,    "override_level_music"    },
8390   { TYPE_INTEGER,&si.volume_simple,           "volume_simple"           },
8391   { TYPE_INTEGER,&si.volume_loops,            "volume_loops"            },
8392   { TYPE_INTEGER,&si.volume_music,            "volume_music"            },
8393   { TYPE_STRING, &si.touch.control_type,      "touch.control_type"      },
8394   { TYPE_INTEGER,&si.touch.move_distance,     "touch.move_distance"     },
8395   { TYPE_INTEGER,&si.touch.drop_distance,     "touch.drop_distance"     },
8396 };
8397
8398 static struct TokenInfo editor_setup_tokens[] =
8399 {
8400   { TYPE_SWITCH, &sei.el_classic,       "editor.el_classic"             },
8401   { TYPE_SWITCH, &sei.el_custom,        "editor.el_custom"              },
8402   { TYPE_SWITCH, &sei.el_user_defined,  "editor.el_user_defined"        },
8403   { TYPE_SWITCH, &sei.el_dynamic,       "editor.el_dynamic"             },
8404   { TYPE_SWITCH, &sei.el_headlines,     "editor.el_headlines"           },
8405   { TYPE_SWITCH, &sei.show_element_token,"editor.show_element_token"    },
8406 };
8407
8408 static struct TokenInfo editor_cascade_setup_tokens[] =
8409 {
8410   { TYPE_SWITCH, &seci.el_bd,           "editor.cascade.el_bd"          },
8411   { TYPE_SWITCH, &seci.el_em,           "editor.cascade.el_em"          },
8412   { TYPE_SWITCH, &seci.el_emc,          "editor.cascade.el_emc"         },
8413   { TYPE_SWITCH, &seci.el_rnd,          "editor.cascade.el_rnd"         },
8414   { TYPE_SWITCH, &seci.el_sb,           "editor.cascade.el_sb"          },
8415   { TYPE_SWITCH, &seci.el_sp,           "editor.cascade.el_sp"          },
8416   { TYPE_SWITCH, &seci.el_dc,           "editor.cascade.el_dc"          },
8417   { TYPE_SWITCH, &seci.el_dx,           "editor.cascade.el_dx"          },
8418   { TYPE_SWITCH, &seci.el_mm,           "editor.cascade.el_mm"          },
8419   { TYPE_SWITCH, &seci.el_df,           "editor.cascade.el_df"          },
8420   { TYPE_SWITCH, &seci.el_chars,        "editor.cascade.el_chars"       },
8421   { TYPE_SWITCH, &seci.el_steel_chars,  "editor.cascade.el_steel_chars" },
8422   { TYPE_SWITCH, &seci.el_ce,           "editor.cascade.el_ce"          },
8423   { TYPE_SWITCH, &seci.el_ge,           "editor.cascade.el_ge"          },
8424   { TYPE_SWITCH, &seci.el_ref,          "editor.cascade.el_ref"         },
8425   { TYPE_SWITCH, &seci.el_user,         "editor.cascade.el_user"        },
8426   { TYPE_SWITCH, &seci.el_dynamic,      "editor.cascade.el_dynamic"     },
8427 };
8428
8429 static struct TokenInfo shortcut_setup_tokens[] =
8430 {
8431   { TYPE_KEY_X11, &ssi.save_game,       "shortcut.save_game"            },
8432   { TYPE_KEY_X11, &ssi.load_game,       "shortcut.load_game"            },
8433   { TYPE_KEY_X11, &ssi.toggle_pause,    "shortcut.toggle_pause"         },
8434   { TYPE_KEY_X11, &ssi.focus_player[0], "shortcut.focus_player_1"       },
8435   { TYPE_KEY_X11, &ssi.focus_player[1], "shortcut.focus_player_2"       },
8436   { TYPE_KEY_X11, &ssi.focus_player[2], "shortcut.focus_player_3"       },
8437   { TYPE_KEY_X11, &ssi.focus_player[3], "shortcut.focus_player_4"       },
8438   { TYPE_KEY_X11, &ssi.focus_player_all,"shortcut.focus_player_all"     },
8439   { TYPE_KEY_X11, &ssi.tape_eject,      "shortcut.tape_eject"           },
8440   { TYPE_KEY_X11, &ssi.tape_extra,      "shortcut.tape_extra"           },
8441   { TYPE_KEY_X11, &ssi.tape_stop,       "shortcut.tape_stop"            },
8442   { TYPE_KEY_X11, &ssi.tape_pause,      "shortcut.tape_pause"           },
8443   { TYPE_KEY_X11, &ssi.tape_record,     "shortcut.tape_record"          },
8444   { TYPE_KEY_X11, &ssi.tape_play,       "shortcut.tape_play"            },
8445   { TYPE_KEY_X11, &ssi.sound_simple,    "shortcut.sound_simple"         },
8446   { TYPE_KEY_X11, &ssi.sound_loops,     "shortcut.sound_loops"          },
8447   { TYPE_KEY_X11, &ssi.sound_music,     "shortcut.sound_music"          },
8448   { TYPE_KEY_X11, &ssi.snap_left,       "shortcut.snap_left"            },
8449   { TYPE_KEY_X11, &ssi.snap_right,      "shortcut.snap_right"           },
8450   { TYPE_KEY_X11, &ssi.snap_up,         "shortcut.snap_up"              },
8451   { TYPE_KEY_X11, &ssi.snap_down,       "shortcut.snap_down"            },
8452 };
8453
8454 static struct TokenInfo player_setup_tokens[] =
8455 {
8456   { TYPE_BOOLEAN, &sii.use_joystick,    ".use_joystick"                 },
8457   { TYPE_STRING,  &sii.joy.device_name, ".joy.device_name"              },
8458   { TYPE_INTEGER, &sii.joy.xleft,       ".joy.xleft"                    },
8459   { TYPE_INTEGER, &sii.joy.xmiddle,     ".joy.xmiddle"                  },
8460   { TYPE_INTEGER, &sii.joy.xright,      ".joy.xright"                   },
8461   { TYPE_INTEGER, &sii.joy.yupper,      ".joy.yupper"                   },
8462   { TYPE_INTEGER, &sii.joy.ymiddle,     ".joy.ymiddle"                  },
8463   { TYPE_INTEGER, &sii.joy.ylower,      ".joy.ylower"                   },
8464   { TYPE_INTEGER, &sii.joy.snap,        ".joy.snap_field"               },
8465   { TYPE_INTEGER, &sii.joy.drop,        ".joy.place_bomb"               },
8466   { TYPE_KEY_X11, &sii.key.left,        ".key.move_left"                },
8467   { TYPE_KEY_X11, &sii.key.right,       ".key.move_right"               },
8468   { TYPE_KEY_X11, &sii.key.up,          ".key.move_up"                  },
8469   { TYPE_KEY_X11, &sii.key.down,        ".key.move_down"                },
8470   { TYPE_KEY_X11, &sii.key.snap,        ".key.snap_field"               },
8471   { TYPE_KEY_X11, &sii.key.drop,        ".key.place_bomb"               },
8472 };
8473
8474 static struct TokenInfo system_setup_tokens[] =
8475 {
8476   { TYPE_STRING,  &syi.sdl_videodriver,    "system.sdl_videodriver"     },
8477   { TYPE_STRING,  &syi.sdl_audiodriver,    "system.sdl_audiodriver"     },
8478   { TYPE_INTEGER, &syi.audio_fragment_size,"system.audio_fragment_size" },
8479 };
8480
8481 static struct TokenInfo internal_setup_tokens[] =
8482 {
8483   { TYPE_STRING, &sxi.program_title,            "program_title"         },
8484   { TYPE_STRING, &sxi.program_version,          "program_version"       },
8485   { TYPE_STRING, &sxi.program_author,           "program_author"        },
8486   { TYPE_STRING, &sxi.program_email,            "program_email"         },
8487   { TYPE_STRING, &sxi.program_website,          "program_website"       },
8488   { TYPE_STRING, &sxi.program_copyright,        "program_copyright"     },
8489   { TYPE_STRING, &sxi.program_company,          "program_company"       },
8490   { TYPE_STRING, &sxi.program_icon_file,        "program_icon_file"     },
8491   { TYPE_STRING, &sxi.default_graphics_set,     "default_graphics_set"  },
8492   { TYPE_STRING, &sxi.default_sounds_set,       "default_sounds_set"    },
8493   { TYPE_STRING, &sxi.default_music_set,        "default_music_set"     },
8494   { TYPE_STRING, &sxi.fallback_graphics_file,   "fallback_graphics_file"},
8495   { TYPE_STRING, &sxi.fallback_sounds_file,     "fallback_sounds_file"  },
8496   { TYPE_STRING, &sxi.fallback_music_file,      "fallback_music_file"   },
8497   { TYPE_STRING, &sxi.default_level_series,     "default_level_series"  },
8498   { TYPE_BOOLEAN,&sxi.choose_from_top_leveldir, "choose_from_top_leveldir" },
8499   { TYPE_BOOLEAN,&sxi.show_scaling_in_title,    "show_scaling_in_title" },
8500   { TYPE_INTEGER,&sxi.default_window_width,     "default_window_width"  },
8501   { TYPE_INTEGER,&sxi.default_window_height,    "default_window_height" },
8502 };
8503
8504 static struct TokenInfo debug_setup_tokens[] =
8505 {
8506   { TYPE_INTEGER, &sdi.frame_delay[0],          "debug.frame_delay_0"   },
8507   { TYPE_INTEGER, &sdi.frame_delay[1],          "debug.frame_delay_1"   },
8508   { TYPE_INTEGER, &sdi.frame_delay[2],          "debug.frame_delay_2"   },
8509   { TYPE_INTEGER, &sdi.frame_delay[3],          "debug.frame_delay_3"   },
8510   { TYPE_INTEGER, &sdi.frame_delay[4],          "debug.frame_delay_4"   },
8511   { TYPE_INTEGER, &sdi.frame_delay[5],          "debug.frame_delay_5"   },
8512   { TYPE_INTEGER, &sdi.frame_delay[6],          "debug.frame_delay_6"   },
8513   { TYPE_INTEGER, &sdi.frame_delay[7],          "debug.frame_delay_7"   },
8514   { TYPE_INTEGER, &sdi.frame_delay[8],          "debug.frame_delay_8"   },
8515   { TYPE_INTEGER, &sdi.frame_delay[9],          "debug.frame_delay_9"   },
8516   { TYPE_KEY_X11, &sdi.frame_delay_key[0],      "debug.key.frame_delay_0" },
8517   { TYPE_KEY_X11, &sdi.frame_delay_key[1],      "debug.key.frame_delay_1" },
8518   { TYPE_KEY_X11, &sdi.frame_delay_key[2],      "debug.key.frame_delay_2" },
8519   { TYPE_KEY_X11, &sdi.frame_delay_key[3],      "debug.key.frame_delay_3" },
8520   { TYPE_KEY_X11, &sdi.frame_delay_key[4],      "debug.key.frame_delay_4" },
8521   { TYPE_KEY_X11, &sdi.frame_delay_key[5],      "debug.key.frame_delay_5" },
8522   { TYPE_KEY_X11, &sdi.frame_delay_key[6],      "debug.key.frame_delay_6" },
8523   { TYPE_KEY_X11, &sdi.frame_delay_key[7],      "debug.key.frame_delay_7" },
8524   { TYPE_KEY_X11, &sdi.frame_delay_key[8],      "debug.key.frame_delay_8" },
8525   { TYPE_KEY_X11, &sdi.frame_delay_key[9],      "debug.key.frame_delay_9" },
8526   { TYPE_BOOLEAN, &sdi.frame_delay_use_mod_key,"debug.frame_delay.use_mod_key"},
8527   { TYPE_BOOLEAN, &sdi.frame_delay_game_only,  "debug.frame_delay.game_only" },
8528   { TYPE_BOOLEAN, &sdi.show_frames_per_second, "debug.show_frames_per_second" },
8529 };
8530
8531 static struct TokenInfo options_setup_tokens[] =
8532 {
8533   { TYPE_BOOLEAN, &soi.verbose,         "options.verbose"               },
8534 };
8535
8536 static char *get_corrected_login_name(char *login_name)
8537 {
8538   /* needed because player name must be a fixed length string */
8539   char *login_name_new = checked_malloc(MAX_PLAYER_NAME_LEN + 1);
8540
8541   strncpy(login_name_new, login_name, MAX_PLAYER_NAME_LEN);
8542   login_name_new[MAX_PLAYER_NAME_LEN] = '\0';
8543
8544   if (strlen(login_name) > MAX_PLAYER_NAME_LEN)         /* name has been cut */
8545     if (strchr(login_name_new, ' '))
8546       *strchr(login_name_new, ' ') = '\0';
8547
8548   return login_name_new;
8549 }
8550
8551 static void setSetupInfoToDefaults(struct SetupInfo *si)
8552 {
8553   int i;
8554
8555   si->player_name = get_corrected_login_name(getLoginName());
8556
8557   si->sound = TRUE;
8558   si->sound_loops = TRUE;
8559   si->sound_music = TRUE;
8560   si->sound_simple = TRUE;
8561   si->toons = TRUE;
8562   si->scroll_delay = TRUE;
8563   si->scroll_delay_value = STD_SCROLL_DELAY;
8564   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
8565   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
8566   si->fade_screens = TRUE;
8567   si->autorecord = TRUE;
8568   si->show_titlescreen = TRUE;
8569   si->quick_doors = FALSE;
8570   si->team_mode = FALSE;
8571   si->handicap = TRUE;
8572   si->skip_levels = TRUE;
8573   si->increment_levels = TRUE;
8574   si->time_limit = TRUE;
8575   si->fullscreen = FALSE;
8576   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
8577   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
8578   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
8579   si->ask_on_escape = TRUE;
8580   si->ask_on_escape_editor = TRUE;
8581   si->quick_switch = FALSE;
8582   si->input_on_focus = FALSE;
8583   si->prefer_aga_graphics = TRUE;
8584   si->game_frame_delay = GAME_FRAME_DELAY;
8585   si->sp_show_border_elements = FALSE;
8586   si->small_game_graphics = FALSE;
8587   si->show_snapshot_buttons = FALSE;
8588
8589   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8590   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8591   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8592
8593   si->override_level_graphics = FALSE;
8594   si->override_level_sounds = FALSE;
8595   si->override_level_music = FALSE;
8596
8597   si->volume_simple = 100;              /* percent */
8598   si->volume_loops = 100;               /* percent */
8599   si->volume_music = 100;               /* percent */
8600
8601   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
8602   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        /* percent */
8603   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        /* percent */
8604
8605   si->editor.el_boulderdash             = TRUE;
8606   si->editor.el_emerald_mine            = TRUE;
8607   si->editor.el_emerald_mine_club       = TRUE;
8608   si->editor.el_more                    = TRUE;
8609   si->editor.el_sokoban                 = TRUE;
8610   si->editor.el_supaplex                = TRUE;
8611   si->editor.el_diamond_caves           = TRUE;
8612   si->editor.el_dx_boulderdash          = TRUE;
8613
8614   si->editor.el_mirror_magic            = TRUE;
8615   si->editor.el_deflektor               = TRUE;
8616
8617   si->editor.el_chars                   = TRUE;
8618   si->editor.el_steel_chars             = TRUE;
8619
8620   si->editor.el_classic                 = TRUE;
8621   si->editor.el_custom                  = TRUE;
8622
8623   si->editor.el_user_defined            = FALSE;
8624   si->editor.el_dynamic                 = TRUE;
8625
8626   si->editor.el_headlines               = TRUE;
8627
8628   si->editor.show_element_token         = FALSE;
8629
8630   si->editor.use_template_for_new_levels = TRUE;
8631
8632   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
8633   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
8634   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
8635
8636   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
8637   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
8638   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
8639   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
8640   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
8641
8642   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
8643   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
8644   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
8645   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
8646   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
8647   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
8648
8649   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
8650   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
8651   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
8652
8653   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
8654   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
8655   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
8656   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
8657
8658   for (i = 0; i < MAX_PLAYERS; i++)
8659   {
8660     si->input[i].use_joystick = FALSE;
8661     si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
8662     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
8663     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
8664     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
8665     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
8666     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
8667     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
8668     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
8669     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
8670     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
8671     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
8672     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
8673     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
8674     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
8675     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
8676   }
8677
8678   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
8679   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
8680   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
8681
8682   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
8683   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
8684   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
8685   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
8686   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
8687   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
8688   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
8689
8690   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
8691
8692   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
8693   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
8694   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
8695
8696   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
8697   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
8698   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
8699
8700   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
8701   si->internal.choose_from_top_leveldir = FALSE;
8702   si->internal.show_scaling_in_title = TRUE;
8703
8704   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
8705   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
8706
8707   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
8708   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
8709   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
8710   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
8711   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
8712   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
8713   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
8714   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
8715   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
8716   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
8717
8718   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
8719   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
8720   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
8721   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
8722   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
8723   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
8724   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
8725   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
8726   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
8727   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
8728
8729   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
8730   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
8731
8732   si->debug.show_frames_per_second = FALSE;
8733
8734   si->options.verbose = FALSE;
8735
8736 #if defined(PLATFORM_ANDROID)
8737   si->fullscreen = TRUE;
8738 #endif
8739 }
8740
8741 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
8742 {
8743   si->editor_cascade.el_bd              = TRUE;
8744   si->editor_cascade.el_em              = TRUE;
8745   si->editor_cascade.el_emc             = TRUE;
8746   si->editor_cascade.el_rnd             = TRUE;
8747   si->editor_cascade.el_sb              = TRUE;
8748   si->editor_cascade.el_sp              = TRUE;
8749   si->editor_cascade.el_dc              = TRUE;
8750   si->editor_cascade.el_dx              = TRUE;
8751
8752   si->editor_cascade.el_mm              = TRUE;
8753   si->editor_cascade.el_df              = TRUE;
8754
8755   si->editor_cascade.el_chars           = FALSE;
8756   si->editor_cascade.el_steel_chars     = FALSE;
8757   si->editor_cascade.el_ce              = FALSE;
8758   si->editor_cascade.el_ge              = FALSE;
8759   si->editor_cascade.el_ref             = FALSE;
8760   si->editor_cascade.el_user            = FALSE;
8761   si->editor_cascade.el_dynamic         = FALSE;
8762 }
8763
8764 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
8765
8766 static char *getHideSetupToken(void *setup_value)
8767 {
8768   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
8769
8770   if (setup_value != NULL)
8771     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
8772
8773   return hide_setup_token;
8774 }
8775
8776 static void setHideSetupEntry(void *setup_value_raw)
8777 {
8778   /* !!! DIRTY WORKAROUND; TO BE FIXED AFTER THE MM ENGINE RELEASE !!! */
8779   void *setup_value = setup_value_raw - (void *)&si + (void *)&setup;
8780
8781   char *hide_setup_token = getHideSetupToken(setup_value);
8782
8783   if (setup_value != NULL)
8784     setHashEntry(hide_setup_hash, hide_setup_token, "");
8785 }
8786
8787 boolean hideSetupEntry(void *setup_value)
8788 {
8789   char *hide_setup_token = getHideSetupToken(setup_value);
8790
8791   return (setup_value != NULL &&
8792           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
8793 }
8794
8795 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
8796                                       struct TokenInfo *token_info,
8797                                       int token_nr, char *token_text)
8798 {
8799   char *token_hide_text = getStringCat2(token_text, ".hide");
8800   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
8801
8802   /* set the value of this setup option in the setup option structure */
8803   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
8804
8805   /* check if this setup option should be hidden in the setup menu */
8806   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
8807     setHideSetupEntry(token_info[token_nr].value);
8808 }
8809
8810 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
8811                                       struct TokenInfo *token_info,
8812                                       int token_nr)
8813 {
8814   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
8815                             token_info[token_nr].text);
8816 }
8817
8818 static void decodeSetupFileHash(SetupFileHash *setup_file_hash)
8819 {
8820   int i, pnr;
8821
8822   if (!setup_file_hash)
8823     return;
8824
8825   if (hide_setup_hash == NULL)
8826     hide_setup_hash = newSetupFileHash();
8827
8828   /* global setup */
8829   si = setup;
8830   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
8831     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
8832   setup = si;
8833
8834   /* editor setup */
8835   sei = setup.editor;
8836   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
8837     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
8838   setup.editor = sei;
8839
8840   /* shortcut setup */
8841   ssi = setup.shortcut;
8842   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
8843     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
8844   setup.shortcut = ssi;
8845
8846   /* player setup */
8847   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
8848   {
8849     char prefix[30];
8850
8851     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
8852
8853     sii = setup.input[pnr];
8854     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
8855     {
8856       char full_token[100];
8857
8858       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
8859       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
8860                                 full_token);
8861     }
8862     setup.input[pnr] = sii;
8863   }
8864
8865   /* system setup */
8866   syi = setup.system;
8867   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
8868     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
8869   setup.system = syi;
8870
8871   /* internal setup */
8872   sxi = setup.internal;
8873   for (i = 0; i < NUM_INTERNAL_SETUP_TOKENS; i++)
8874     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
8875   setup.internal = sxi;
8876
8877   /* debug setup */
8878   sdi = setup.debug;
8879   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
8880     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
8881   setup.debug = sdi;
8882
8883   /* options setup */
8884   soi = setup.options;
8885   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
8886     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
8887   setup.options = soi;
8888 }
8889
8890 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
8891 {
8892   int i;
8893
8894   if (!setup_file_hash)
8895     return;
8896
8897   /* editor cascade setup */
8898   seci = setup.editor_cascade;
8899   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
8900     setSetupInfo(editor_cascade_setup_tokens, i,
8901                  getHashEntry(setup_file_hash,
8902                               editor_cascade_setup_tokens[i].text));
8903   setup.editor_cascade = seci;
8904 }
8905
8906 void LoadSetupFromFilename(char *filename)
8907 {
8908   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
8909
8910   if (setup_file_hash)
8911   {
8912     decodeSetupFileHash(setup_file_hash);
8913
8914     freeSetupFileHash(setup_file_hash);
8915   }
8916   else
8917   {
8918     Error(ERR_DEBUG, "using default setup values");
8919   }
8920 }
8921
8922 static void LoadSetup_SpecialPostProcessing()
8923 {
8924   char *player_name_new;
8925
8926   /* needed to work around problems with fixed length strings */
8927   player_name_new = get_corrected_login_name(setup.player_name);
8928   free(setup.player_name);
8929   setup.player_name = player_name_new;
8930
8931   /* "scroll_delay: on(3) / off(0)" was replaced by scroll delay value */
8932   if (setup.scroll_delay == FALSE)
8933   {
8934     setup.scroll_delay_value = MIN_SCROLL_DELAY;
8935     setup.scroll_delay = TRUE;                  /* now always "on" */
8936   }
8937
8938   /* make sure that scroll delay value stays inside valid range */
8939   setup.scroll_delay_value =
8940     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
8941 }
8942
8943 void LoadSetup()
8944 {
8945   char *filename;
8946
8947   /* always start with reliable default values */
8948   setSetupInfoToDefaults(&setup);
8949
8950   /* try to load setup values from default setup file */
8951   filename = getDefaultSetupFilename();
8952
8953   if (fileExists(filename))
8954     LoadSetupFromFilename(filename);
8955
8956   /* try to load setup values from user setup file */
8957   filename = getSetupFilename();
8958
8959   LoadSetupFromFilename(filename);
8960
8961   LoadSetup_SpecialPostProcessing();
8962 }
8963
8964 void LoadSetup_EditorCascade()
8965 {
8966   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
8967   SetupFileHash *setup_file_hash = NULL;
8968
8969   /* always start with reliable default values */
8970   setSetupInfoToDefaults_EditorCascade(&setup);
8971
8972   setup_file_hash = loadSetupFileHash(filename);
8973
8974   if (setup_file_hash)
8975   {
8976     decodeSetupFileHash_EditorCascade(setup_file_hash);
8977
8978     freeSetupFileHash(setup_file_hash);
8979   }
8980
8981   free(filename);
8982 }
8983
8984 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
8985                                            char *mapping_line)
8986 {
8987   char mapping_guid[MAX_LINE_LEN];
8988   char *mapping_start, *mapping_end;
8989
8990   // get GUID from game controller mapping line: copy complete line
8991   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
8992   mapping_guid[MAX_LINE_LEN - 1] = '\0';
8993
8994   // get GUID from game controller mapping line: cut after GUID part
8995   mapping_start = strchr(mapping_guid, ',');
8996   if (mapping_start != NULL)
8997     *mapping_start = '\0';
8998
8999   // cut newline from game controller mapping line
9000   mapping_end = strchr(mapping_line, '\n');
9001   if (mapping_end != NULL)
9002     *mapping_end = '\0';
9003
9004   // add mapping entry to game controller mappings hash
9005   setHashEntry(mappings_hash, mapping_guid, mapping_line);
9006 }
9007
9008 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
9009                                                  char *filename)
9010 {
9011   FILE *file;
9012
9013   if (!(file = fopen(filename, MODE_READ)))
9014   {
9015     Error(ERR_WARN, "cannot read game controller mappings file '%s'", filename);
9016
9017     return;
9018   }
9019
9020   while (!feof(file))
9021   {
9022     char line[MAX_LINE_LEN];
9023
9024     if (!fgets(line, MAX_LINE_LEN, file))
9025       break;
9026
9027     addGameControllerMappingToHash(mappings_hash, line);
9028   }
9029
9030   fclose(file);
9031 }
9032
9033 void SaveSetup()
9034 {
9035   char *filename = getSetupFilename();
9036   FILE *file;
9037   int i, pnr;
9038
9039   InitUserDataDirectory();
9040
9041   if (!(file = fopen(filename, MODE_WRITE)))
9042   {
9043     Error(ERR_WARN, "cannot write setup file '%s'", filename);
9044     return;
9045   }
9046
9047   fprintFileHeader(file, SETUP_FILENAME);
9048
9049   /* global setup */
9050   si = setup;
9051   for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
9052   {
9053     /* just to make things nicer :) */
9054     if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
9055         i == SETUP_TOKEN_GRAPHICS_SET ||
9056         i == SETUP_TOKEN_VOLUME_SIMPLE ||
9057         i == SETUP_TOKEN_TOUCH_CONTROL_TYPE)
9058       fprintf(file, "\n");
9059
9060     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
9061   }
9062
9063   /* editor setup */
9064   sei = setup.editor;
9065   fprintf(file, "\n");
9066   for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
9067     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
9068
9069   /* shortcut setup */
9070   ssi = setup.shortcut;
9071   fprintf(file, "\n");
9072   for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
9073     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
9074
9075   /* player setup */
9076   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
9077   {
9078     char prefix[30];
9079
9080     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
9081     fprintf(file, "\n");
9082
9083     sii = setup.input[pnr];
9084     for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
9085       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
9086   }
9087
9088   /* system setup */
9089   syi = setup.system;
9090   fprintf(file, "\n");
9091   for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
9092     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
9093
9094   /* internal setup */
9095   /* (internal setup values not saved to user setup file) */
9096
9097   /* debug setup */
9098   sdi = setup.debug;
9099   fprintf(file, "\n");
9100   for (i = 0; i < NUM_DEBUG_SETUP_TOKENS; i++)
9101     fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
9102
9103   /* options setup */
9104   soi = setup.options;
9105   fprintf(file, "\n");
9106   for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
9107     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
9108
9109   fclose(file);
9110
9111   SetFilePermissions(filename, PERMS_PRIVATE);
9112 }
9113
9114 void SaveSetup_EditorCascade()
9115 {
9116   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
9117   FILE *file;
9118   int i;
9119
9120   InitUserDataDirectory();
9121
9122   if (!(file = fopen(filename, MODE_WRITE)))
9123   {
9124     Error(ERR_WARN, "cannot write editor cascade state file '%s'", filename);
9125     free(filename);
9126     return;
9127   }
9128
9129   fprintFileHeader(file, EDITORCASCADE_FILENAME);
9130
9131   seci = setup.editor_cascade;
9132   for (i = 0; i < NUM_EDITOR_CASCADE_SETUP_TOKENS; i++)
9133     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
9134
9135   fclose(file);
9136
9137   SetFilePermissions(filename, PERMS_PRIVATE);
9138
9139   free(filename);
9140 }
9141
9142 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
9143                                                   char *filename)
9144 {
9145   FILE *file;
9146
9147   if (!(file = fopen(filename, MODE_WRITE)))
9148   {
9149     Error(ERR_WARN, "cannot write game controller mappings file '%s'",filename);
9150
9151     return;
9152   }
9153
9154   BEGIN_HASH_ITERATION(mappings_hash, itr)
9155   {
9156     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
9157   }
9158   END_HASH_ITERATION(mappings_hash, itr)
9159
9160   fclose(file);
9161 }
9162
9163 void SaveSetup_AddGameControllerMapping(char *mapping)
9164 {
9165   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
9166   SetupFileHash *mappings_hash = newSetupFileHash();
9167
9168   InitUserDataDirectory();
9169
9170   // load existing personal game controller mappings
9171   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
9172
9173   // add new mapping to personal game controller mappings
9174   addGameControllerMappingToHash(mappings_hash, mapping);
9175
9176   // save updated personal game controller mappings
9177   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
9178
9179   freeSetupFileHash(mappings_hash);
9180   free(filename);
9181 }
9182
9183 void LoadCustomElementDescriptions()
9184 {
9185   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9186   SetupFileHash *setup_file_hash;
9187   int i;
9188
9189   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9190   {
9191     if (element_info[i].custom_description != NULL)
9192     {
9193       free(element_info[i].custom_description);
9194       element_info[i].custom_description = NULL;
9195     }
9196   }
9197
9198   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9199     return;
9200
9201   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9202   {
9203     char *token = getStringCat2(element_info[i].token_name, ".name");
9204     char *value = getHashEntry(setup_file_hash, token);
9205
9206     if (value != NULL)
9207       element_info[i].custom_description = getStringCopy(value);
9208
9209     free(token);
9210   }
9211
9212   freeSetupFileHash(setup_file_hash);
9213 }
9214
9215 static int getElementFromToken(char *token)
9216 {
9217   char *value = getHashEntry(element_token_hash, token);
9218
9219   if (value != NULL)
9220     return atoi(value);
9221
9222   Error(ERR_WARN, "unknown element token '%s'", token);
9223
9224   return EL_UNDEFINED;
9225 }
9226
9227 static int get_token_parameter_value(char *token, char *value_raw)
9228 {
9229   char *suffix;
9230
9231   if (token == NULL || value_raw == NULL)
9232     return ARG_UNDEFINED_VALUE;
9233
9234   suffix = strrchr(token, '.');
9235   if (suffix == NULL)
9236     suffix = token;
9237
9238   if (strEqual(suffix, ".element"))
9239     return getElementFromToken(value_raw);
9240
9241   /* !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!! */
9242   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
9243 }
9244
9245 void InitMenuDesignSettings_Static()
9246 {
9247   int i;
9248
9249   /* always start with reliable default values from static default config */
9250   for (i = 0; image_config_vars[i].token != NULL; i++)
9251   {
9252     char *value = getHashEntry(image_config_hash, image_config_vars[i].token);
9253
9254     if (value != NULL)
9255       *image_config_vars[i].value =
9256         get_token_parameter_value(image_config_vars[i].token, value);
9257   }
9258 }
9259
9260 static void InitMenuDesignSettings_SpecialPreProcessing()
9261 {
9262   int i;
9263
9264   /* the following initializes hierarchical values from static configuration */
9265
9266   /* special case: initialize "ARG_DEFAULT" values in static default config */
9267   /* (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode") */
9268   titlescreen_initial_first_default.fade_mode  =
9269     title_initial_first_default.fade_mode;
9270   titlescreen_initial_first_default.fade_delay =
9271     title_initial_first_default.fade_delay;
9272   titlescreen_initial_first_default.post_delay =
9273     title_initial_first_default.post_delay;
9274   titlescreen_initial_first_default.auto_delay =
9275     title_initial_first_default.auto_delay;
9276   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
9277   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
9278   titlescreen_first_default.post_delay = title_first_default.post_delay;
9279   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
9280   titlemessage_initial_first_default.fade_mode  =
9281     title_initial_first_default.fade_mode;
9282   titlemessage_initial_first_default.fade_delay =
9283     title_initial_first_default.fade_delay;
9284   titlemessage_initial_first_default.post_delay =
9285     title_initial_first_default.post_delay;
9286   titlemessage_initial_first_default.auto_delay =
9287     title_initial_first_default.auto_delay;
9288   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
9289   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
9290   titlemessage_first_default.post_delay = title_first_default.post_delay;
9291   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
9292
9293   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
9294   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
9295   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
9296   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
9297   titlescreen_default.fade_mode  = title_default.fade_mode;
9298   titlescreen_default.fade_delay = title_default.fade_delay;
9299   titlescreen_default.post_delay = title_default.post_delay;
9300   titlescreen_default.auto_delay = title_default.auto_delay;
9301   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
9302   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
9303   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
9304   titlemessage_initial_default.auto_delay = title_initial_default.auto_delay;
9305   titlemessage_default.fade_mode  = title_default.fade_mode;
9306   titlemessage_default.fade_delay = title_default.fade_delay;
9307   titlemessage_default.post_delay = title_default.post_delay;
9308   titlemessage_default.auto_delay = title_default.auto_delay;
9309
9310   /* special case: initialize "ARG_DEFAULT" values in static default config */
9311   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9312   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
9313   {
9314     titlescreen_initial_first[i] = titlescreen_initial_first_default;
9315     titlescreen_first[i] = titlescreen_first_default;
9316     titlemessage_initial_first[i] = titlemessage_initial_first_default;
9317     titlemessage_first[i] = titlemessage_first_default;
9318
9319     titlescreen_initial[i] = titlescreen_initial_default;
9320     titlescreen[i] = titlescreen_default;
9321     titlemessage_initial[i] = titlemessage_initial_default;
9322     titlemessage[i] = titlemessage_default;
9323   }
9324
9325   /* special case: initialize "ARG_DEFAULT" values in static default config */
9326   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9327   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9328   {
9329     if (i == GFX_SPECIAL_ARG_TITLE)     /* title values already initialized */
9330       continue;
9331
9332     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
9333     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
9334     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
9335   }
9336
9337   /* special case: initialize "ARG_DEFAULT" values in static default config */
9338   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9339   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9340   {
9341     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
9342     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
9343     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
9344
9345     if (i == GFX_SPECIAL_ARG_EDITOR)    /* editor values already initialized */
9346       continue;
9347
9348     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
9349   }
9350 }
9351
9352 static void InitMenuDesignSettings_SpecialPostProcessing()
9353 {
9354   static struct
9355   {
9356     struct XY *dst, *src;
9357   }
9358   game_buttons_xy[] =
9359   {
9360     { &game.button.save,        &game.button.stop       },
9361     { &game.button.pause2,      &game.button.pause      },
9362     { &game.button.load,        &game.button.play       },
9363     { &game.button.undo,        &game.button.stop       },
9364     { &game.button.redo,        &game.button.play       },
9365
9366     { NULL,                     NULL                    }
9367   };
9368   int i;
9369
9370   /* special case: initialize later added SETUP list size from LEVELS value */
9371   if (menu.list_size[GAME_MODE_SETUP] == -1)
9372     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
9373
9374   /* set default position for snapshot buttons to stop/pause/play buttons */
9375   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
9376     if ((*game_buttons_xy[i].dst).x == -1 &&
9377         (*game_buttons_xy[i].dst).y == -1)
9378       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
9379 }
9380
9381 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics()
9382 {
9383   static struct
9384   {
9385     struct XYTileSize *dst, *src;
9386     int graphic;
9387   }
9388   editor_buttons_xy[] =
9389   {
9390     {
9391       &editor.button.element_left,      &editor.palette.element_left,
9392       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
9393     },
9394     {
9395       &editor.button.element_middle,    &editor.palette.element_middle,
9396       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
9397     },
9398     {
9399       &editor.button.element_right,     &editor.palette.element_right,
9400       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
9401     },
9402
9403     { NULL,                     NULL                    }
9404   };
9405   int i;
9406
9407   /* set default position for element buttons to element graphics */
9408   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
9409   {
9410     if ((*editor_buttons_xy[i].dst).x == -1 &&
9411         (*editor_buttons_xy[i].dst).y == -1)
9412     {
9413       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
9414
9415       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
9416
9417       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
9418     }
9419   }
9420 }
9421
9422 static void LoadMenuDesignSettingsFromFilename(char *filename)
9423 {
9424   static struct TitleFadingInfo tfi;
9425   static struct TitleMessageInfo tmi;
9426   static struct TokenInfo title_tokens[] =
9427   {
9428     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
9429     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
9430     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
9431     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
9432
9433     { -1,               NULL,                   NULL                    }
9434   };
9435   static struct TokenInfo titlemessage_tokens[] =
9436   {
9437     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
9438     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
9439     { TYPE_INTEGER,     &tmi.width,             ".width"                },
9440     { TYPE_INTEGER,     &tmi.height,            ".height"               },
9441     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
9442     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
9443     { TYPE_INTEGER,     &tmi.align,             ".align"                },
9444     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
9445     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
9446     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
9447     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
9448     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
9449     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
9450     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
9451     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
9452     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
9453     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
9454
9455     { -1,               NULL,                   NULL                    }
9456   };
9457   static struct
9458   {
9459     struct TitleFadingInfo *info;
9460     char *text;
9461   }
9462   title_info[] =
9463   {
9464     /* initialize first titles from "enter screen" definitions, if defined */
9465     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
9466     { &title_first_default,             "menu.enter_screen.TITLE"       },
9467
9468     /* initialize title screens from "next screen" definitions, if defined */
9469     { &title_initial_default,           "menu.next_screen.TITLE"        },
9470     { &title_default,                   "menu.next_screen.TITLE"        },
9471
9472     { NULL,                             NULL                            }
9473   };
9474   static struct
9475   {
9476     struct TitleMessageInfo *array;
9477     char *text;
9478   }
9479   titlemessage_arrays[] =
9480   {
9481     /* initialize first titles from "enter screen" definitions, if defined */
9482     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
9483     { titlescreen_first,                "menu.enter_screen.TITLE"       },
9484     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
9485     { titlemessage_first,               "menu.enter_screen.TITLE"       },
9486
9487     /* initialize titles from "next screen" definitions, if defined */
9488     { titlescreen_initial,              "menu.next_screen.TITLE"        },
9489     { titlescreen,                      "menu.next_screen.TITLE"        },
9490     { titlemessage_initial,             "menu.next_screen.TITLE"        },
9491     { titlemessage,                     "menu.next_screen.TITLE"        },
9492
9493     /* overwrite titles with title definitions, if defined */
9494     { titlescreen_initial_first,        "[title_initial]"               },
9495     { titlescreen_first,                "[title]"                       },
9496     { titlemessage_initial_first,       "[title_initial]"               },
9497     { titlemessage_first,               "[title]"                       },
9498
9499     { titlescreen_initial,              "[title_initial]"               },
9500     { titlescreen,                      "[title]"                       },
9501     { titlemessage_initial,             "[title_initial]"               },
9502     { titlemessage,                     "[title]"                       },
9503
9504     /* overwrite titles with title screen/message definitions, if defined */
9505     { titlescreen_initial_first,        "[titlescreen_initial]"         },
9506     { titlescreen_first,                "[titlescreen]"                 },
9507     { titlemessage_initial_first,       "[titlemessage_initial]"        },
9508     { titlemessage_first,               "[titlemessage]"                },
9509
9510     { titlescreen_initial,              "[titlescreen_initial]"         },
9511     { titlescreen,                      "[titlescreen]"                 },
9512     { titlemessage_initial,             "[titlemessage_initial]"        },
9513     { titlemessage,                     "[titlemessage]"                },
9514
9515     { NULL,                             NULL                            }
9516   };
9517   SetupFileHash *setup_file_hash;
9518   int i, j, k;
9519
9520   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
9521     return;
9522
9523   /* the following initializes hierarchical values from dynamic configuration */
9524
9525   /* special case: initialize with default values that may be overwritten */
9526   /* (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset") */
9527   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9528   {
9529     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset");
9530     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset");
9531     char *value_3 = getHashEntry(setup_file_hash, "menu.list_size");
9532
9533     if (value_1 != NULL)
9534       menu.draw_xoffset[i] = get_integer_from_string(value_1);
9535     if (value_2 != NULL)
9536       menu.draw_yoffset[i] = get_integer_from_string(value_2);
9537     if (value_3 != NULL)
9538       menu.list_size[i] = get_integer_from_string(value_3);
9539   }
9540
9541   /* special case: initialize with default values that may be overwritten */
9542   /* (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO") */
9543   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
9544   {
9545     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.INFO");
9546     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.INFO");
9547
9548     if (value_1 != NULL)
9549       menu.draw_xoffset_info[i] = get_integer_from_string(value_1);
9550     if (value_2 != NULL)
9551       menu.draw_yoffset_info[i] = get_integer_from_string(value_2);
9552
9553     if (i == GFX_SPECIAL_ARG_INFO_ELEMENTS)
9554     {
9555       char *value_1 = getHashEntry(setup_file_hash, "menu.list_size.INFO");
9556
9557       if (value_1 != NULL)
9558         menu.list_size_info[i] = get_integer_from_string(value_1);
9559     }
9560   }
9561
9562   /* special case: initialize with default values that may be overwritten */
9563   /* (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP") */
9564   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
9565   {
9566     char *value_1 = getHashEntry(setup_file_hash, "menu.draw_xoffset.SETUP");
9567     char *value_2 = getHashEntry(setup_file_hash, "menu.draw_yoffset.SETUP");
9568
9569     if (value_1 != NULL)
9570       menu.draw_xoffset_setup[i] = get_integer_from_string(value_1);
9571     if (value_2 != NULL)
9572       menu.draw_yoffset_setup[i] = get_integer_from_string(value_2);
9573   }
9574
9575   /* special case: initialize with default values that may be overwritten */
9576   /* (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz") */
9577   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9578   {
9579     char *token_1 = "menu.enter_screen.fade_mode";
9580     char *token_2 = "menu.enter_screen.fade_delay";
9581     char *token_3 = "menu.enter_screen.post_delay";
9582     char *token_4 = "menu.leave_screen.fade_mode";
9583     char *token_5 = "menu.leave_screen.fade_delay";
9584     char *token_6 = "menu.leave_screen.post_delay";
9585     char *token_7 = "menu.next_screen.fade_mode";
9586     char *token_8 = "menu.next_screen.fade_delay";
9587     char *token_9 = "menu.next_screen.post_delay";
9588     char *value_1 = getHashEntry(setup_file_hash, token_1);
9589     char *value_2 = getHashEntry(setup_file_hash, token_2);
9590     char *value_3 = getHashEntry(setup_file_hash, token_3);
9591     char *value_4 = getHashEntry(setup_file_hash, token_4);
9592     char *value_5 = getHashEntry(setup_file_hash, token_5);
9593     char *value_6 = getHashEntry(setup_file_hash, token_6);
9594     char *value_7 = getHashEntry(setup_file_hash, token_7);
9595     char *value_8 = getHashEntry(setup_file_hash, token_8);
9596     char *value_9 = getHashEntry(setup_file_hash, token_9);
9597
9598     if (value_1 != NULL)
9599       menu.enter_screen[i].fade_mode = get_token_parameter_value(token_1,
9600                                                                  value_1);
9601     if (value_2 != NULL)
9602       menu.enter_screen[i].fade_delay = get_token_parameter_value(token_2,
9603                                                                   value_2);
9604     if (value_3 != NULL)
9605       menu.enter_screen[i].post_delay = get_token_parameter_value(token_3,
9606                                                                   value_3);
9607     if (value_4 != NULL)
9608       menu.leave_screen[i].fade_mode = get_token_parameter_value(token_4,
9609                                                                  value_4);
9610     if (value_5 != NULL)
9611       menu.leave_screen[i].fade_delay = get_token_parameter_value(token_5,
9612                                                                   value_5);
9613     if (value_6 != NULL)
9614       menu.leave_screen[i].post_delay = get_token_parameter_value(token_6,
9615                                                                   value_6);
9616     if (value_7 != NULL)
9617       menu.next_screen[i].fade_mode = get_token_parameter_value(token_7,
9618                                                                 value_7);
9619     if (value_8 != NULL)
9620       menu.next_screen[i].fade_delay = get_token_parameter_value(token_8,
9621                                                                  value_8);
9622     if (value_9 != NULL)
9623       menu.next_screen[i].post_delay = get_token_parameter_value(token_9,
9624                                                                  value_9);
9625   }
9626
9627   /* special case: initialize with default values that may be overwritten */
9628   /* (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz") */
9629   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
9630   {
9631     char *token_w1 = "viewport.window.width";
9632     char *token_w2 = "viewport.window.height";
9633     char *token_01 = "viewport.playfield.x";
9634     char *token_02 = "viewport.playfield.y";
9635     char *token_03 = "viewport.playfield.width";
9636     char *token_04 = "viewport.playfield.height";
9637     char *token_05 = "viewport.playfield.border_size";
9638     char *token_06 = "viewport.door_1.x";
9639     char *token_07 = "viewport.door_1.y";
9640     char *token_08 = "viewport.door_1.width";
9641     char *token_09 = "viewport.door_1.height";
9642     char *token_10 = "viewport.door_1.border_size";
9643     char *token_11 = "viewport.door_2.x";
9644     char *token_12 = "viewport.door_2.y";
9645     char *token_13 = "viewport.door_2.width";
9646     char *token_14 = "viewport.door_2.height";
9647     char *token_15 = "viewport.door_2.border_size";
9648     char *value_w1 = getHashEntry(setup_file_hash, token_w1);
9649     char *value_w2 = getHashEntry(setup_file_hash, token_w2);
9650     char *value_01 = getHashEntry(setup_file_hash, token_01);
9651     char *value_02 = getHashEntry(setup_file_hash, token_02);
9652     char *value_03 = getHashEntry(setup_file_hash, token_03);
9653     char *value_04 = getHashEntry(setup_file_hash, token_04);
9654     char *value_05 = getHashEntry(setup_file_hash, token_05);
9655     char *value_06 = getHashEntry(setup_file_hash, token_06);
9656     char *value_07 = getHashEntry(setup_file_hash, token_07);
9657     char *value_08 = getHashEntry(setup_file_hash, token_08);
9658     char *value_09 = getHashEntry(setup_file_hash, token_09);
9659     char *value_10 = getHashEntry(setup_file_hash, token_10);
9660     char *value_11 = getHashEntry(setup_file_hash, token_11);
9661     char *value_12 = getHashEntry(setup_file_hash, token_12);
9662     char *value_13 = getHashEntry(setup_file_hash, token_13);
9663     char *value_14 = getHashEntry(setup_file_hash, token_14);
9664     char *value_15 = getHashEntry(setup_file_hash, token_15);
9665
9666     if (value_w1 != NULL)
9667       viewport.window[i].width = get_token_parameter_value(token_w1, value_w1);
9668     if (value_w2 != NULL)
9669       viewport.window[i].height = get_token_parameter_value(token_w2, value_w2);
9670     if (value_01 != NULL)
9671       viewport.playfield[i].x = get_token_parameter_value(token_01, value_01);
9672     if (value_02 != NULL)
9673       viewport.playfield[i].y = get_token_parameter_value(token_02, value_02);
9674     if (value_03 != NULL)
9675       viewport.playfield[i].width = get_token_parameter_value(token_03,
9676                                                               value_03);
9677     if (value_04 != NULL)
9678       viewport.playfield[i].height = get_token_parameter_value(token_04,
9679                                                                value_04);
9680     if (value_05 != NULL)
9681       viewport.playfield[i].border_size = get_token_parameter_value(token_05,
9682                                                                     value_05);
9683     if (value_06 != NULL)
9684       viewport.door_1[i].x = get_token_parameter_value(token_06, value_06);
9685     if (value_07 != NULL)
9686       viewport.door_1[i].y = get_token_parameter_value(token_07, value_07);
9687     if (value_08 != NULL)
9688       viewport.door_1[i].width = get_token_parameter_value(token_08, value_08);
9689     if (value_09 != NULL)
9690       viewport.door_1[i].height = get_token_parameter_value(token_09, value_09);
9691     if (value_10 != NULL)
9692       viewport.door_1[i].border_size = get_token_parameter_value(token_10,
9693                                                                  value_10);
9694     if (value_11 != NULL)
9695       viewport.door_2[i].x = get_token_parameter_value(token_11, value_11);
9696     if (value_12 != NULL)
9697       viewport.door_2[i].y = get_token_parameter_value(token_12, value_12);
9698     if (value_13 != NULL)
9699       viewport.door_2[i].width = get_token_parameter_value(token_13, value_13);
9700     if (value_14 != NULL)
9701       viewport.door_2[i].height = get_token_parameter_value(token_14, value_14);
9702     if (value_15 != NULL)
9703       viewport.door_1[i].border_size = get_token_parameter_value(token_15,
9704                                                                  value_15);
9705   }
9706
9707   /* special case: initialize with default values that may be overwritten */
9708   /* (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode") */
9709   for (i = 0; title_info[i].info != NULL; i++)
9710   {
9711     struct TitleFadingInfo *info = title_info[i].info;
9712     char *base_token = title_info[i].text;
9713
9714     for (j = 0; title_tokens[j].type != -1; j++)
9715     {
9716       char *token = getStringCat2(base_token, title_tokens[j].text);
9717       char *value = getHashEntry(setup_file_hash, token);
9718
9719       if (value != NULL)
9720       {
9721         int parameter_value = get_token_parameter_value(token, value);
9722
9723         tfi = *info;
9724
9725         *(int *)title_tokens[j].value = (int)parameter_value;
9726
9727         *info = tfi;
9728       }
9729
9730       free(token);
9731     }
9732   }
9733
9734   /* special case: initialize with default values that may be overwritten */
9735   /* (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode") */
9736   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
9737   {
9738     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
9739     char *base_token = titlemessage_arrays[i].text;
9740
9741     for (j = 0; titlemessage_tokens[j].type != -1; j++)
9742     {
9743       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
9744       char *value = getHashEntry(setup_file_hash, token);
9745
9746       if (value != NULL)
9747       {
9748         int parameter_value = get_token_parameter_value(token, value);
9749
9750         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
9751         {
9752           tmi = array[k];
9753
9754           if (titlemessage_tokens[j].type == TYPE_INTEGER)
9755             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
9756           else
9757             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
9758
9759           array[k] = tmi;
9760         }
9761       }
9762
9763       free(token);
9764     }
9765   }
9766
9767   /* read (and overwrite with) values that may be specified in config file */
9768   for (i = 0; image_config_vars[i].token != NULL; i++)
9769   {
9770     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
9771
9772     /* (ignore definitions set to "[DEFAULT]" which are already initialized) */
9773     if (value != NULL && !strEqual(value, ARG_DEFAULT))
9774       *image_config_vars[i].value =
9775         get_token_parameter_value(image_config_vars[i].token, value);
9776   }
9777
9778   freeSetupFileHash(setup_file_hash);
9779 }
9780
9781 void LoadMenuDesignSettings()
9782 {
9783   char *filename_base = UNDEFINED_FILENAME, *filename_local;
9784
9785   InitMenuDesignSettings_Static();
9786   InitMenuDesignSettings_SpecialPreProcessing();
9787
9788   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
9789   {
9790     /* first look for special settings configured in level series config */
9791     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
9792
9793     if (fileExists(filename_base))
9794       LoadMenuDesignSettingsFromFilename(filename_base);
9795   }
9796
9797   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
9798
9799   if (filename_local != NULL && !strEqual(filename_base, filename_local))
9800     LoadMenuDesignSettingsFromFilename(filename_local);
9801
9802   InitMenuDesignSettings_SpecialPostProcessing();
9803 }
9804
9805 void LoadMenuDesignSettings_AfterGraphics()
9806 {
9807   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
9808 }
9809
9810 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
9811 {
9812   char *filename = getEditorSetupFilename();
9813   SetupFileList *setup_file_list, *list;
9814   SetupFileHash *element_hash;
9815   int num_unknown_tokens = 0;
9816   int i;
9817
9818   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
9819     return;
9820
9821   element_hash = newSetupFileHash();
9822
9823   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
9824     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
9825
9826   /* determined size may be larger than needed (due to unknown elements) */
9827   *num_elements = 0;
9828   for (list = setup_file_list; list != NULL; list = list->next)
9829     (*num_elements)++;
9830
9831   /* add space for up to 3 more elements for padding that may be needed */
9832   *num_elements += 3;
9833
9834   /* free memory for old list of elements, if needed */
9835   checked_free(*elements);
9836
9837   /* allocate memory for new list of elements */
9838   *elements = checked_malloc(*num_elements * sizeof(int));
9839
9840   *num_elements = 0;
9841   for (list = setup_file_list; list != NULL; list = list->next)
9842   {
9843     char *value = getHashEntry(element_hash, list->token);
9844
9845     if (value == NULL)          /* try to find obsolete token mapping */
9846     {
9847       char *mapped_token = get_mapped_token(list->token);
9848
9849       if (mapped_token != NULL)
9850       {
9851         value = getHashEntry(element_hash, mapped_token);
9852
9853         free(mapped_token);
9854       }
9855     }
9856
9857     if (value != NULL)
9858     {
9859       (*elements)[(*num_elements)++] = atoi(value);
9860     }
9861     else
9862     {
9863       if (num_unknown_tokens == 0)
9864       {
9865         Error(ERR_INFO_LINE, "-");
9866         Error(ERR_INFO, "warning: unknown token(s) found in config file:");
9867         Error(ERR_INFO, "- config file: '%s'", filename);
9868
9869         num_unknown_tokens++;
9870       }
9871
9872       Error(ERR_INFO, "- token: '%s'", list->token);
9873     }
9874   }
9875
9876   if (num_unknown_tokens > 0)
9877     Error(ERR_INFO_LINE, "-");
9878
9879   while (*num_elements % 4)     /* pad with empty elements, if needed */
9880     (*elements)[(*num_elements)++] = EL_EMPTY;
9881
9882   freeSetupFileList(setup_file_list);
9883   freeSetupFileHash(element_hash);
9884
9885 #if 0
9886   for (i = 0; i < *num_elements; i++)
9887     printf("editor: element '%s' [%d]\n",
9888            element_info[(*elements)[i]].token_name, (*elements)[i]);
9889 #endif
9890 }
9891
9892 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
9893                                                      boolean is_sound)
9894 {
9895   SetupFileHash *setup_file_hash = NULL;
9896   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
9897   char *filename_music, *filename_prefix, *filename_info;
9898   struct
9899   {
9900     char *token;
9901     char **value_ptr;
9902   }
9903   token_to_value_ptr[] =
9904   {
9905     { "title_header",   &tmp_music_file_info.title_header       },
9906     { "artist_header",  &tmp_music_file_info.artist_header      },
9907     { "album_header",   &tmp_music_file_info.album_header       },
9908     { "year_header",    &tmp_music_file_info.year_header        },
9909
9910     { "title",          &tmp_music_file_info.title              },
9911     { "artist",         &tmp_music_file_info.artist             },
9912     { "album",          &tmp_music_file_info.album              },
9913     { "year",           &tmp_music_file_info.year               },
9914
9915     { NULL,             NULL                                    },
9916   };
9917   int i;
9918
9919   filename_music = (is_sound ? getCustomSoundFilename(basename) :
9920                     getCustomMusicFilename(basename));
9921
9922   if (filename_music == NULL)
9923     return NULL;
9924
9925   /* ---------- try to replace file extension ---------- */
9926
9927   filename_prefix = getStringCopy(filename_music);
9928   if (strrchr(filename_prefix, '.') != NULL)
9929     *strrchr(filename_prefix, '.') = '\0';
9930   filename_info = getStringCat2(filename_prefix, ".txt");
9931
9932   if (fileExists(filename_info))
9933     setup_file_hash = loadSetupFileHash(filename_info);
9934
9935   free(filename_prefix);
9936   free(filename_info);
9937
9938   if (setup_file_hash == NULL)
9939   {
9940     /* ---------- try to add file extension ---------- */
9941
9942     filename_prefix = getStringCopy(filename_music);
9943     filename_info = getStringCat2(filename_prefix, ".txt");
9944
9945     if (fileExists(filename_info))
9946       setup_file_hash = loadSetupFileHash(filename_info);
9947
9948     free(filename_prefix);
9949     free(filename_info);
9950   }
9951
9952   if (setup_file_hash == NULL)
9953     return NULL;
9954
9955   /* ---------- music file info found ---------- */
9956
9957   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
9958
9959   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
9960   {
9961     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
9962
9963     *token_to_value_ptr[i].value_ptr =
9964       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
9965   }
9966
9967   tmp_music_file_info.basename = getStringCopy(basename);
9968   tmp_music_file_info.music = music;
9969   tmp_music_file_info.is_sound = is_sound;
9970
9971   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
9972   *new_music_file_info = tmp_music_file_info;
9973
9974   return new_music_file_info;
9975 }
9976
9977 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
9978 {
9979   return get_music_file_info_ext(basename, music, FALSE);
9980 }
9981
9982 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
9983 {
9984   return get_music_file_info_ext(basename, sound, TRUE);
9985 }
9986
9987 static boolean music_info_listed_ext(struct MusicFileInfo *list,
9988                                      char *basename, boolean is_sound)
9989 {
9990   for (; list != NULL; list = list->next)
9991     if (list->is_sound == is_sound && strEqual(list->basename, basename))
9992       return TRUE;
9993
9994   return FALSE;
9995 }
9996
9997 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
9998 {
9999   return music_info_listed_ext(list, basename, FALSE);
10000 }
10001
10002 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
10003 {
10004   return music_info_listed_ext(list, basename, TRUE);
10005 }
10006
10007 void LoadMusicInfo()
10008 {
10009   char *music_directory = getCustomMusicDirectory();
10010   int num_music = getMusicListSize();
10011   int num_music_noconf = 0;
10012   int num_sounds = getSoundListSize();
10013   Directory *dir;
10014   DirectoryEntry *dir_entry;
10015   struct FileInfo *music, *sound;
10016   struct MusicFileInfo *next, **new;
10017   int i;
10018
10019   while (music_file_info != NULL)
10020   {
10021     next = music_file_info->next;
10022
10023     checked_free(music_file_info->basename);
10024
10025     checked_free(music_file_info->title_header);
10026     checked_free(music_file_info->artist_header);
10027     checked_free(music_file_info->album_header);
10028     checked_free(music_file_info->year_header);
10029
10030     checked_free(music_file_info->title);
10031     checked_free(music_file_info->artist);
10032     checked_free(music_file_info->album);
10033     checked_free(music_file_info->year);
10034
10035     free(music_file_info);
10036
10037     music_file_info = next;
10038   }
10039
10040   new = &music_file_info;
10041
10042   for (i = 0; i < num_music; i++)
10043   {
10044     music = getMusicListEntry(i);
10045
10046     if (music->filename == NULL)
10047       continue;
10048
10049     if (strEqual(music->filename, UNDEFINED_FILENAME))
10050       continue;
10051
10052     /* a configured file may be not recognized as music */
10053     if (!FileIsMusic(music->filename))
10054       continue;
10055
10056     if (!music_info_listed(music_file_info, music->filename))
10057     {
10058       *new = get_music_file_info(music->filename, i);
10059
10060       if (*new != NULL)
10061         new = &(*new)->next;
10062     }
10063   }
10064
10065   if ((dir = openDirectory(music_directory)) == NULL)
10066   {
10067     Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
10068     return;
10069   }
10070
10071   while ((dir_entry = readDirectory(dir)) != NULL)      /* loop all entries */
10072   {
10073     char *basename = dir_entry->basename;
10074     boolean music_already_used = FALSE;
10075     int i;
10076
10077     /* skip all music files that are configured in music config file */
10078     for (i = 0; i < num_music; i++)
10079     {
10080       music = getMusicListEntry(i);
10081
10082       if (music->filename == NULL)
10083         continue;
10084
10085       if (strEqual(basename, music->filename))
10086       {
10087         music_already_used = TRUE;
10088         break;
10089       }
10090     }
10091
10092     if (music_already_used)
10093       continue;
10094
10095     if (!FileIsMusic(dir_entry->filename))
10096       continue;
10097
10098     if (!music_info_listed(music_file_info, basename))
10099     {
10100       *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
10101
10102       if (*new != NULL)
10103         new = &(*new)->next;
10104     }
10105
10106     num_music_noconf++;
10107   }
10108
10109   closeDirectory(dir);
10110
10111   for (i = 0; i < num_sounds; i++)
10112   {
10113     sound = getSoundListEntry(i);
10114
10115     if (sound->filename == NULL)
10116       continue;
10117
10118     if (strEqual(sound->filename, UNDEFINED_FILENAME))
10119       continue;
10120
10121     /* a configured file may be not recognized as sound */
10122     if (!FileIsSound(sound->filename))
10123       continue;
10124
10125     if (!sound_info_listed(music_file_info, sound->filename))
10126     {
10127       *new = get_sound_file_info(sound->filename, i);
10128       if (*new != NULL)
10129         new = &(*new)->next;
10130     }
10131   }
10132 }
10133
10134 void add_helpanim_entry(int element, int action, int direction, int delay,
10135                         int *num_list_entries)
10136 {
10137   struct HelpAnimInfo *new_list_entry;
10138   (*num_list_entries)++;
10139
10140   helpanim_info =
10141     checked_realloc(helpanim_info,
10142                     *num_list_entries * sizeof(struct HelpAnimInfo));
10143   new_list_entry = &helpanim_info[*num_list_entries - 1];
10144
10145   new_list_entry->element = element;
10146   new_list_entry->action = action;
10147   new_list_entry->direction = direction;
10148   new_list_entry->delay = delay;
10149 }
10150
10151 void print_unknown_token(char *filename, char *token, int token_nr)
10152 {
10153   if (token_nr == 0)
10154   {
10155     Error(ERR_INFO_LINE, "-");
10156     Error(ERR_INFO, "warning: unknown token(s) found in config file:");
10157     Error(ERR_INFO, "- config file: '%s'", filename);
10158   }
10159
10160   Error(ERR_INFO, "- token: '%s'", token);
10161 }
10162
10163 void print_unknown_token_end(int token_nr)
10164 {
10165   if (token_nr > 0)
10166     Error(ERR_INFO_LINE, "-");
10167 }
10168
10169 void LoadHelpAnimInfo()
10170 {
10171   char *filename = getHelpAnimFilename();
10172   SetupFileList *setup_file_list = NULL, *list;
10173   SetupFileHash *element_hash, *action_hash, *direction_hash;
10174   int num_list_entries = 0;
10175   int num_unknown_tokens = 0;
10176   int i;
10177
10178   if (fileExists(filename))
10179     setup_file_list = loadSetupFileList(filename);
10180
10181   if (setup_file_list == NULL)
10182   {
10183     /* use reliable default values from static configuration */
10184     SetupFileList *insert_ptr;
10185
10186     insert_ptr = setup_file_list =
10187       newSetupFileList(helpanim_config[0].token,
10188                        helpanim_config[0].value);
10189
10190     for (i = 1; helpanim_config[i].token; i++)
10191       insert_ptr = addListEntry(insert_ptr,
10192                                 helpanim_config[i].token,
10193                                 helpanim_config[i].value);
10194   }
10195
10196   element_hash   = newSetupFileHash();
10197   action_hash    = newSetupFileHash();
10198   direction_hash = newSetupFileHash();
10199
10200   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
10201     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
10202
10203   for (i = 0; i < NUM_ACTIONS; i++)
10204     setHashEntry(action_hash, element_action_info[i].suffix,
10205                  i_to_a(element_action_info[i].value));
10206
10207   /* do not store direction index (bit) here, but direction value! */
10208   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
10209     setHashEntry(direction_hash, element_direction_info[i].suffix,
10210                  i_to_a(1 << element_direction_info[i].value));
10211
10212   for (list = setup_file_list; list != NULL; list = list->next)
10213   {
10214     char *element_token, *action_token, *direction_token;
10215     char *element_value, *action_value, *direction_value;
10216     int delay = atoi(list->value);
10217
10218     if (strEqual(list->token, "end"))
10219     {
10220       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10221
10222       continue;
10223     }
10224
10225     /* first try to break element into element/action/direction parts;
10226        if this does not work, also accept combined "element[.act][.dir]"
10227        elements (like "dynamite.active"), which are unique elements */
10228
10229     if (strchr(list->token, '.') == NULL)       /* token contains no '.' */
10230     {
10231       element_value = getHashEntry(element_hash, list->token);
10232       if (element_value != NULL)        /* element found */
10233         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10234                            &num_list_entries);
10235       else
10236       {
10237         /* no further suffixes found -- this is not an element */
10238         print_unknown_token(filename, list->token, num_unknown_tokens++);
10239       }
10240
10241       continue;
10242     }
10243
10244     /* token has format "<prefix>.<something>" */
10245
10246     action_token = strchr(list->token, '.');    /* suffix may be action ... */
10247     direction_token = action_token;             /* ... or direction */
10248
10249     element_token = getStringCopy(list->token);
10250     *strchr(element_token, '.') = '\0';
10251
10252     element_value = getHashEntry(element_hash, element_token);
10253
10254     if (element_value == NULL)          /* this is no element */
10255     {
10256       element_value = getHashEntry(element_hash, list->token);
10257       if (element_value != NULL)        /* combined element found */
10258         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10259                            &num_list_entries);
10260       else
10261         print_unknown_token(filename, list->token, num_unknown_tokens++);
10262
10263       free(element_token);
10264
10265       continue;
10266     }
10267
10268     action_value = getHashEntry(action_hash, action_token);
10269
10270     if (action_value != NULL)           /* action found */
10271     {
10272       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
10273                     &num_list_entries);
10274
10275       free(element_token);
10276
10277       continue;
10278     }
10279
10280     direction_value = getHashEntry(direction_hash, direction_token);
10281
10282     if (direction_value != NULL)        /* direction found */
10283     {
10284       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
10285                          &num_list_entries);
10286
10287       free(element_token);
10288
10289       continue;
10290     }
10291
10292     if (strchr(action_token + 1, '.') == NULL)
10293     {
10294       /* no further suffixes found -- this is not an action nor direction */
10295
10296       element_value = getHashEntry(element_hash, list->token);
10297       if (element_value != NULL)        /* combined element found */
10298         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10299                            &num_list_entries);
10300       else
10301         print_unknown_token(filename, list->token, num_unknown_tokens++);
10302
10303       free(element_token);
10304
10305       continue;
10306     }
10307
10308     /* token has format "<prefix>.<suffix>.<something>" */
10309
10310     direction_token = strchr(action_token + 1, '.');
10311
10312     action_token = getStringCopy(action_token);
10313     *strchr(action_token + 1, '.') = '\0';
10314
10315     action_value = getHashEntry(action_hash, action_token);
10316
10317     if (action_value == NULL)           /* this is no action */
10318     {
10319       element_value = getHashEntry(element_hash, list->token);
10320       if (element_value != NULL)        /* combined element found */
10321         add_helpanim_entry(atoi(element_value), -1, -1, delay,
10322                            &num_list_entries);
10323       else
10324         print_unknown_token(filename, list->token, num_unknown_tokens++);
10325
10326       free(element_token);
10327       free(action_token);
10328
10329       continue;
10330     }
10331
10332     direction_value = getHashEntry(direction_hash, direction_token);
10333
10334     if (direction_value != NULL)        /* direction found */
10335     {
10336       add_helpanim_entry(atoi(element_value), atoi(action_value),
10337                          atoi(direction_value), delay, &num_list_entries);
10338
10339       free(element_token);
10340       free(action_token);
10341
10342       continue;
10343     }
10344
10345     /* this is no direction */
10346
10347     element_value = getHashEntry(element_hash, list->token);
10348     if (element_value != NULL)          /* combined element found */
10349       add_helpanim_entry(atoi(element_value), -1, -1, delay,
10350                          &num_list_entries);
10351     else
10352       print_unknown_token(filename, list->token, num_unknown_tokens++);
10353
10354     free(element_token);
10355     free(action_token);
10356   }
10357
10358   print_unknown_token_end(num_unknown_tokens);
10359
10360   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
10361   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
10362
10363   freeSetupFileList(setup_file_list);
10364   freeSetupFileHash(element_hash);
10365   freeSetupFileHash(action_hash);
10366   freeSetupFileHash(direction_hash);
10367
10368 #if 0
10369   for (i = 0; i < num_list_entries; i++)
10370     printf("::: '%s': %d, %d, %d => %d\n",
10371            EL_NAME(helpanim_info[i].element),
10372            helpanim_info[i].element,
10373            helpanim_info[i].action,
10374            helpanim_info[i].direction,
10375            helpanim_info[i].delay);
10376 #endif
10377 }
10378
10379 void LoadHelpTextInfo()
10380 {
10381   char *filename = getHelpTextFilename();
10382   int i;
10383
10384   if (helptext_info != NULL)
10385   {
10386     freeSetupFileHash(helptext_info);
10387     helptext_info = NULL;
10388   }
10389
10390   if (fileExists(filename))
10391     helptext_info = loadSetupFileHash(filename);
10392
10393   if (helptext_info == NULL)
10394   {
10395     /* use reliable default values from static configuration */
10396     helptext_info = newSetupFileHash();
10397
10398     for (i = 0; helptext_config[i].token; i++)
10399       setHashEntry(helptext_info,
10400                    helptext_config[i].token,
10401                    helptext_config[i].value);
10402   }
10403
10404 #if 0
10405   BEGIN_HASH_ITERATION(helptext_info, itr)
10406   {
10407     printf("::: '%s' => '%s'\n",
10408            HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
10409   }
10410   END_HASH_ITERATION(hash, itr)
10411 #endif
10412 }
10413
10414
10415 /* ------------------------------------------------------------------------- */
10416 /* convert levels                                                            */
10417 /* ------------------------------------------------------------------------- */
10418
10419 #define MAX_NUM_CONVERT_LEVELS          1000
10420
10421 void ConvertLevels()
10422 {
10423   static LevelDirTree *convert_leveldir = NULL;
10424   static int convert_level_nr = -1;
10425   static int num_levels_handled = 0;
10426   static int num_levels_converted = 0;
10427   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
10428   int i;
10429
10430   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
10431                                                global.convert_leveldir);
10432
10433   if (convert_leveldir == NULL)
10434     Error(ERR_EXIT, "no such level identifier: '%s'",
10435           global.convert_leveldir);
10436
10437   leveldir_current = convert_leveldir;
10438
10439   if (global.convert_level_nr != -1)
10440   {
10441     convert_leveldir->first_level = global.convert_level_nr;
10442     convert_leveldir->last_level  = global.convert_level_nr;
10443   }
10444
10445   convert_level_nr = convert_leveldir->first_level;
10446
10447   PrintLine("=", 79);
10448   Print("Converting levels\n");
10449   PrintLine("-", 79);
10450   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
10451   Print("Level series name:       '%s'\n", convert_leveldir->name);
10452   Print("Level series author:     '%s'\n", convert_leveldir->author);
10453   Print("Number of levels:        %d\n",   convert_leveldir->levels);
10454   PrintLine("=", 79);
10455   Print("\n");
10456
10457   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10458     levels_failed[i] = FALSE;
10459
10460   while (convert_level_nr <= convert_leveldir->last_level)
10461   {
10462     char *level_filename;
10463     boolean new_level;
10464
10465     level_nr = convert_level_nr++;
10466
10467     Print("Level %03d: ", level_nr);
10468
10469     LoadLevel(level_nr);
10470     if (level.no_level_file || level.no_valid_file)
10471     {
10472       Print("(no level)\n");
10473       continue;
10474     }
10475
10476     Print("converting level ... ");
10477
10478     level_filename = getDefaultLevelFilename(level_nr);
10479     new_level = !fileExists(level_filename);
10480
10481     if (new_level)
10482     {
10483       SaveLevel(level_nr);
10484
10485       num_levels_converted++;
10486
10487       Print("converted.\n");
10488     }
10489     else
10490     {
10491       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
10492         levels_failed[level_nr] = TRUE;
10493
10494       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
10495     }
10496
10497     num_levels_handled++;
10498   }
10499
10500   Print("\n");
10501   PrintLine("=", 79);
10502   Print("Number of levels handled: %d\n", num_levels_handled);
10503   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
10504          (num_levels_handled ?
10505           num_levels_converted * 100 / num_levels_handled : 0));
10506   PrintLine("-", 79);
10507   Print("Summary (for automatic parsing by scripts):\n");
10508   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
10509          convert_leveldir->identifier, num_levels_converted,
10510          num_levels_handled,
10511          (num_levels_handled ?
10512           num_levels_converted * 100 / num_levels_handled : 0));
10513
10514   if (num_levels_handled != num_levels_converted)
10515   {
10516     Print(", FAILED:");
10517     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
10518       if (levels_failed[i])
10519         Print(" %03d", i);
10520   }
10521
10522   Print("\n");
10523   PrintLine("=", 79);
10524
10525   CloseAllAndExit(0);
10526 }
10527
10528
10529 /* ------------------------------------------------------------------------- */
10530 /* create and save images for use in level sketches (raw BMP format)         */
10531 /* ------------------------------------------------------------------------- */
10532
10533 void CreateLevelSketchImages()
10534 {
10535 #if defined(TARGET_SDL)
10536   Bitmap *bitmap1;
10537   Bitmap *bitmap2;
10538   int i;
10539
10540   InitElementPropertiesGfxElement();
10541
10542   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
10543   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
10544
10545   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
10546   {
10547     Bitmap *src_bitmap;
10548     int src_x, src_y;
10549     int element = getMappedElement(i);
10550     int graphic = el2edimg(element);
10551     char basename1[16];
10552     char basename2[16];
10553     char *filename1;
10554     char *filename2;
10555
10556     sprintf(basename1, "%03d.bmp", i);
10557     sprintf(basename2, "%03ds.bmp", i);
10558
10559     filename1 = getPath2(global.create_images_dir, basename1);
10560     filename2 = getPath2(global.create_images_dir, basename2);
10561
10562     getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
10563     BlitBitmap(src_bitmap, bitmap1, src_x, src_y, TILEX, TILEY,
10564                0, 0);
10565
10566     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
10567       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename1);
10568
10569     getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
10570     BlitBitmap(src_bitmap, bitmap2, src_x, src_y, MINI_TILEX, MINI_TILEY, 0, 0);
10571
10572     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
10573       Error(ERR_EXIT, "cannot save level sketch image file '%s'", filename2);
10574
10575     free(filename1);
10576     free(filename2);
10577
10578     if (options.debug)
10579       printf("%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
10580   }
10581
10582   FreeBitmap(bitmap1);
10583   FreeBitmap(bitmap2);
10584
10585   if (options.debug)
10586     printf("\n");
10587
10588   Error(ERR_INFO, "%d normal and small images created", NUM_FILE_ELEMENTS);
10589
10590   CloseAllAndExit(0);
10591 #endif
10592 }
10593
10594
10595 /* ------------------------------------------------------------------------- */
10596 /* create and save images for custom and group elements (raw BMP format)     */
10597 /* ------------------------------------------------------------------------- */
10598
10599 void CreateCustomElementImages(char *directory)
10600 {
10601 #if defined(TARGET_SDL)
10602   char *src_basename = "RocksCE-template.ilbm";
10603   char *dst_basename = "RocksCE.bmp";
10604   char *src_filename = getPath2(directory, src_basename);
10605   char *dst_filename = getPath2(directory, dst_basename);
10606   Bitmap *src_bitmap;
10607   Bitmap *bitmap;
10608   int yoffset_ce = 0;
10609   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
10610   int i;
10611
10612   SDLInitVideoDisplay();
10613
10614   ReCreateBitmap(&backbuffer, video.width, video.height);
10615
10616   src_bitmap = LoadImage(src_filename);
10617
10618   bitmap = CreateBitmap(TILEX * 16 * 2,
10619                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
10620                         DEFAULT_DEPTH);
10621
10622   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10623   {
10624     int x = i % 16;
10625     int y = i / 16;
10626     int ii = i + 1;
10627     int j;
10628
10629     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10630                TILEX * x, TILEY * y + yoffset_ce);
10631
10632     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10633                TILEX, TILEY,
10634                TILEX * x + TILEX * 16,
10635                TILEY * y + yoffset_ce);
10636
10637     for (j = 2; j >= 0; j--)
10638     {
10639       int c = ii % 10;
10640
10641       BlitBitmap(src_bitmap, bitmap,
10642                  TILEX + c * 7, 0, 6, 10,
10643                  TILEX * x + 6 + j * 7,
10644                  TILEY * y + 11 + yoffset_ce);
10645
10646       BlitBitmap(src_bitmap, bitmap,
10647                  TILEX + c * 8, TILEY, 6, 10,
10648                  TILEX * 16 + TILEX * x + 6 + j * 8,
10649                  TILEY * y + 10 + yoffset_ce);
10650
10651       ii /= 10;
10652     }
10653   }
10654
10655   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
10656   {
10657     int x = i % 16;
10658     int y = i / 16;
10659     int ii = i + 1;
10660     int j;
10661
10662     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
10663                TILEX * x, TILEY * y + yoffset_ge);
10664
10665     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
10666                TILEX, TILEY,
10667                TILEX * x + TILEX * 16,
10668                TILEY * y + yoffset_ge);
10669
10670     for (j = 1; j >= 0; j--)
10671     {
10672       int c = ii % 10;
10673
10674       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
10675                  TILEX * x + 6 + j * 10,
10676                  TILEY * y + 11 + yoffset_ge);
10677
10678       BlitBitmap(src_bitmap, bitmap,
10679                  TILEX + c * 8, TILEY + 12, 6, 10,
10680                  TILEX * 16 + TILEX * x + 10 + j * 8,
10681                  TILEY * y + 10 + yoffset_ge);
10682
10683       ii /= 10;
10684     }
10685   }
10686
10687   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
10688     Error(ERR_EXIT, "cannot save CE graphics file '%s'", dst_filename);
10689
10690   FreeBitmap(bitmap);
10691
10692   CloseAllAndExit(0);
10693 #endif
10694 }