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