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