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