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