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