940ce6edc91e1c1854b9a2fa1bea2b113c2f17d5
[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 //                  https://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 "screens.h"
22 #include "editor.h"
23 #include "tools.h"
24 #include "tape.h"
25 #include "config.h"
26 #include "api.h"
27
28 #define ENABLE_UNUSED_CODE      0       // currently unused functions
29 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
30 #define ENABLE_RESERVED_CODE    0       // reserved for later use
31
32 #define CHUNK_ID_LEN            4       // IFF style chunk id length
33 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
34 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
35
36 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
37 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
38
39 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
40 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
41 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
42 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
43 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
44 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
45 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
46 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
47 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
48 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
49 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
50
51 // (element number, number of change pages, change page number)
52 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
53
54 // (element number only)
55 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
56 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
57 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
58
59 // (nothing at all if unchanged)
60 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
61
62 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
63 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
64 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
65
66 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
67
68 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
69 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
70 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
71
72 // file identifier strings
73 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
74 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
75 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
76
77 // values for deciding when (not) to save configuration data
78 #define SAVE_CONF_NEVER                 0
79 #define SAVE_CONF_ALWAYS                1
80 #define SAVE_CONF_WHEN_CHANGED          -1
81
82 // values for chunks using micro chunks
83 #define CONF_MASK_1_BYTE                0x00
84 #define CONF_MASK_2_BYTE                0x40
85 #define CONF_MASK_4_BYTE                0x80
86 #define CONF_MASK_MULTI_BYTES           0xc0
87
88 #define CONF_MASK_BYTES                 0xc0
89 #define CONF_MASK_TOKEN                 0x3f
90
91 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
92 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
93 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
94 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
95
96 // these definitions are just for convenience of use and readability
97 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
98 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
99 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
100 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
101
102 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
103                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
104                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
105
106 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
107 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
108 #define CONF_ELEMENT_NUM_BYTES          (2)
109
110 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
111                                          (t) == TYPE_ELEMENT_LIST ?     \
112                                          CONF_ELEMENT_NUM_BYTES :       \
113                                          (t) == TYPE_CONTENT ||         \
114                                          (t) == TYPE_CONTENT_LIST ?     \
115                                          CONF_CONTENT_NUM_BYTES : 1)
116
117 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
118 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
119                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
120
121 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
122                                          (y) * 3 + (x))
123 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
124                                          CONF_ELEMENT_NUM_BYTES)
125 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
126                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
127
128 // temporary variables used to store pointers to structure members
129 static struct LevelInfo li;
130 static struct ElementInfo xx_ei, yy_ei;
131 static struct ElementChangeInfo xx_change;
132 static struct ElementGroupInfo xx_group;
133 static struct EnvelopeInfo xx_envelope;
134 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
135 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
136 static int xx_num_contents;
137 static int xx_current_change_page;
138 static char xx_default_string_empty[1] = "";
139 static int xx_string_length_unused;
140
141 struct LevelFileConfigInfo
142 {
143   int element;                  // element for which data is to be stored
144   int save_type;                // save data always, never or when changed
145   int data_type;                // data type (used internally, not stored)
146   int conf_type;                // micro chunk identifier (stored in file)
147
148   // (mandatory)
149   void *value;                  // variable that holds the data to be stored
150   int default_value;            // initial default value for this variable
151
152   // (optional)
153   void *value_copy;             // variable that holds the data to be copied
154   void *num_entities;           // number of entities for multi-byte data
155   int default_num_entities;     // default number of entities for this data
156   int max_num_entities;         // maximal number of entities for this data
157   char *default_string;         // optional default string for string data
158 };
159
160 static struct LevelFileConfigInfo chunk_config_INFO[] =
161 {
162   // ---------- values not related to single elements -------------------------
163
164   {
165     -1,                                 SAVE_CONF_ALWAYS,
166     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
167     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
168   },
169   {
170     -1,                                 SAVE_CONF_ALWAYS,
171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
172     &li.fieldx,                         STD_LEV_FIELDX
173   },
174   {
175     -1,                                 SAVE_CONF_ALWAYS,
176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
177     &li.fieldy,                         STD_LEV_FIELDY
178   },
179   {
180     -1,                                 SAVE_CONF_ALWAYS,
181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
182     &li.time,                           100
183   },
184   {
185     -1,                                 SAVE_CONF_ALWAYS,
186     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
187     &li.gems_needed,                    0
188   },
189   {
190     -1,                                 -1,
191     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
192     &li.random_seed,                    0
193   },
194   {
195     -1,                                 -1,
196     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
197     &li.use_step_counter,               FALSE
198   },
199   {
200     -1,                                 -1,
201     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
202     &li.wind_direction_initial,         MV_NONE
203   },
204   {
205     -1,                                 -1,
206     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
207     &li.em_slippery_gems,               FALSE
208   },
209   {
210     -1,                                 -1,
211     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
212     &li.use_custom_template,            FALSE
213   },
214   {
215     -1,                                 -1,
216     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
217     &li.can_move_into_acid_bits,        ~0      // default: everything can
218   },
219   {
220     -1,                                 -1,
221     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
222     &li.dont_collide_with_bits,         ~0      // default: always deadly
223   },
224   {
225     -1,                                 -1,
226     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
227     &li.em_explodes_by_fire,            FALSE
228   },
229   {
230     -1,                                 -1,
231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
232     &li.score[SC_TIME_BONUS],           1
233   },
234   {
235     -1,                                 -1,
236     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
237     &li.auto_exit_sokoban,              FALSE
238   },
239   {
240     -1,                                 -1,
241     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
242     &li.auto_count_gems,                FALSE
243   },
244   {
245     -1,                                 -1,
246     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
247     &li.solved_by_one_player,           FALSE
248   },
249   {
250     -1,                                 -1,
251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
252     &li.time_score_base,                1
253   },
254   {
255     -1,                                 -1,
256     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
257     &li.rate_time_over_score,           FALSE
258   },
259   {
260     -1,                                 -1,
261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
262     &li.bd_intermission,                FALSE
263   },
264   {
265     -1,                                 -1,
266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
267     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
268   },
269   {
270     -1,                                 -1,
271     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
272     &li.bd_pal_timing,                  FALSE
273   },
274   {
275     -1,                                 -1,
276     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
277     &li.bd_cycle_delay_ms,              160
278   },
279   {
280     -1,                                 -1,
281     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
282     &li.bd_cycle_delay_c64,             0
283   },
284   {
285     -1,                                 -1,
286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
287     &li.bd_hatching_delay_cycles,       21
288   },
289   {
290     -1,                                 -1,
291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
292     &li.bd_hatching_delay_seconds,      2
293   },
294   {
295     -1,                                 -1,
296     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
297     &li.bd_line_shifting_borders,       FALSE
298   },
299   {
300     -1,                                 -1,
301     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
302     &li.bd_scan_first_and_last_row,     TRUE
303   },
304   {
305     -1,                                 -1,
306     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
307     &li.bd_short_explosions,            TRUE
308   },
309   {
310     -1,                                 -1,
311     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
312     &li.bd_cave_random_seed_c64,        0
313   },
314   {
315     -1,                                 -1,
316     TYPE_INTEGER,                       CONF_VALUE_32_BIT(3),
317     &li.bd_color_b,                     GD_C64_COLOR(0)
318   },
319   {
320     -1,                                 -1,
321     TYPE_INTEGER,                       CONF_VALUE_32_BIT(4),
322     &li.bd_color_0,                     GD_C64_COLOR(0)
323   },
324   {
325     -1,                                 -1,
326     TYPE_INTEGER,                       CONF_VALUE_32_BIT(5),
327     &li.bd_color_1,                     GD_C64_COLOR(8)
328   },
329   {
330     -1,                                 -1,
331     TYPE_INTEGER,                       CONF_VALUE_32_BIT(6),
332     &li.bd_color_2,                     GD_C64_COLOR(11)
333   },
334   {
335     -1,                                 -1,
336     TYPE_INTEGER,                       CONF_VALUE_32_BIT(7),
337     &li.bd_color_3,                     GD_C64_COLOR(1)
338   },
339   {
340     -1,                                 -1,
341     TYPE_INTEGER,                       CONF_VALUE_32_BIT(8),
342     &li.bd_color_4,                     GD_C64_COLOR(5)
343   },
344   {
345     -1,                                 -1,
346     TYPE_INTEGER,                       CONF_VALUE_32_BIT(9),
347     &li.bd_color_5,                     GD_C64_COLOR(6)
348   },
349
350   {
351     -1,                                 -1,
352     -1,                                 -1,
353     NULL,                               -1
354   }
355 };
356
357 static struct LevelFileConfigInfo chunk_config_ELEM[] =
358 {
359   // (these values are the same for each player)
360   {
361     EL_PLAYER_1,                        -1,
362     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
363     &li.block_last_field,               FALSE   // default case for EM levels
364   },
365   {
366     EL_PLAYER_1,                        -1,
367     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
368     &li.sp_block_last_field,            TRUE    // default case for SP levels
369   },
370   {
371     EL_PLAYER_1,                        -1,
372     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
373     &li.instant_relocation,             FALSE
374   },
375   {
376     EL_PLAYER_1,                        -1,
377     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
378     &li.can_pass_to_walkable,           FALSE
379   },
380   {
381     EL_PLAYER_1,                        -1,
382     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
383     &li.block_snap_field,               TRUE
384   },
385   {
386     EL_PLAYER_1,                        -1,
387     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
388     &li.continuous_snapping,            TRUE
389   },
390   {
391     EL_PLAYER_1,                        -1,
392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
393     &li.shifted_relocation,             FALSE
394   },
395   {
396     EL_PLAYER_1,                        -1,
397     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
398     &li.lazy_relocation,                FALSE
399   },
400   {
401     EL_PLAYER_1,                        -1,
402     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
403     &li.finish_dig_collect,             TRUE
404   },
405   {
406     EL_PLAYER_1,                        -1,
407     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
408     &li.keep_walkable_ce,               FALSE
409   },
410
411   // (these values are different for each player)
412   {
413     EL_PLAYER_1,                        -1,
414     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
415     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
416   },
417   {
418     EL_PLAYER_1,                        -1,
419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
420     &li.initial_player_gravity[0],      FALSE
421   },
422   {
423     EL_PLAYER_1,                        -1,
424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
425     &li.use_start_element[0],           FALSE
426   },
427   {
428     EL_PLAYER_1,                        -1,
429     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
430     &li.start_element[0],               EL_PLAYER_1
431   },
432   {
433     EL_PLAYER_1,                        -1,
434     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
435     &li.use_artwork_element[0],         FALSE
436   },
437   {
438     EL_PLAYER_1,                        -1,
439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
440     &li.artwork_element[0],             EL_PLAYER_1
441   },
442   {
443     EL_PLAYER_1,                        -1,
444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
445     &li.use_explosion_element[0],       FALSE
446   },
447   {
448     EL_PLAYER_1,                        -1,
449     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
450     &li.explosion_element[0],           EL_PLAYER_1
451   },
452   {
453     EL_PLAYER_1,                        -1,
454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
455     &li.use_initial_inventory[0],       FALSE
456   },
457   {
458     EL_PLAYER_1,                        -1,
459     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
460     &li.initial_inventory_size[0],      1
461   },
462   {
463     EL_PLAYER_1,                        -1,
464     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
465     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
466     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
467   },
468
469   {
470     EL_PLAYER_2,                        -1,
471     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
472     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
473   },
474   {
475     EL_PLAYER_2,                        -1,
476     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
477     &li.initial_player_gravity[1],      FALSE
478   },
479   {
480     EL_PLAYER_2,                        -1,
481     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
482     &li.use_start_element[1],           FALSE
483   },
484   {
485     EL_PLAYER_2,                        -1,
486     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
487     &li.start_element[1],               EL_PLAYER_2
488   },
489   {
490     EL_PLAYER_2,                        -1,
491     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
492     &li.use_artwork_element[1],         FALSE
493   },
494   {
495     EL_PLAYER_2,                        -1,
496     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
497     &li.artwork_element[1],             EL_PLAYER_2
498   },
499   {
500     EL_PLAYER_2,                        -1,
501     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
502     &li.use_explosion_element[1],       FALSE
503   },
504   {
505     EL_PLAYER_2,                        -1,
506     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
507     &li.explosion_element[1],           EL_PLAYER_2
508   },
509   {
510     EL_PLAYER_2,                        -1,
511     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
512     &li.use_initial_inventory[1],       FALSE
513   },
514   {
515     EL_PLAYER_2,                        -1,
516     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
517     &li.initial_inventory_size[1],      1
518   },
519   {
520     EL_PLAYER_2,                        -1,
521     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
522     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
523     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
524   },
525
526   {
527     EL_PLAYER_3,                        -1,
528     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
529     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
530   },
531   {
532     EL_PLAYER_3,                        -1,
533     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
534     &li.initial_player_gravity[2],      FALSE
535   },
536   {
537     EL_PLAYER_3,                        -1,
538     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
539     &li.use_start_element[2],           FALSE
540   },
541   {
542     EL_PLAYER_3,                        -1,
543     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
544     &li.start_element[2],               EL_PLAYER_3
545   },
546   {
547     EL_PLAYER_3,                        -1,
548     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
549     &li.use_artwork_element[2],         FALSE
550   },
551   {
552     EL_PLAYER_3,                        -1,
553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
554     &li.artwork_element[2],             EL_PLAYER_3
555   },
556   {
557     EL_PLAYER_3,                        -1,
558     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
559     &li.use_explosion_element[2],       FALSE
560   },
561   {
562     EL_PLAYER_3,                        -1,
563     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
564     &li.explosion_element[2],           EL_PLAYER_3
565   },
566   {
567     EL_PLAYER_3,                        -1,
568     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
569     &li.use_initial_inventory[2],       FALSE
570   },
571   {
572     EL_PLAYER_3,                        -1,
573     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
574     &li.initial_inventory_size[2],      1
575   },
576   {
577     EL_PLAYER_3,                        -1,
578     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
579     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
580     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
581   },
582
583   {
584     EL_PLAYER_4,                        -1,
585     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
586     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
587   },
588   {
589     EL_PLAYER_4,                        -1,
590     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
591     &li.initial_player_gravity[3],      FALSE
592   },
593   {
594     EL_PLAYER_4,                        -1,
595     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
596     &li.use_start_element[3],           FALSE
597   },
598   {
599     EL_PLAYER_4,                        -1,
600     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
601     &li.start_element[3],               EL_PLAYER_4
602   },
603   {
604     EL_PLAYER_4,                        -1,
605     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
606     &li.use_artwork_element[3],         FALSE
607   },
608   {
609     EL_PLAYER_4,                        -1,
610     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
611     &li.artwork_element[3],             EL_PLAYER_4
612   },
613   {
614     EL_PLAYER_4,                        -1,
615     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
616     &li.use_explosion_element[3],       FALSE
617   },
618   {
619     EL_PLAYER_4,                        -1,
620     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
621     &li.explosion_element[3],           EL_PLAYER_4
622   },
623   {
624     EL_PLAYER_4,                        -1,
625     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
626     &li.use_initial_inventory[3],       FALSE
627   },
628   {
629     EL_PLAYER_4,                        -1,
630     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
631     &li.initial_inventory_size[3],      1
632   },
633   {
634     EL_PLAYER_4,                        -1,
635     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
636     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
637     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
638   },
639
640   // (these values are only valid for BD style levels)
641   // (some values for BD style amoeba following below)
642   {
643     EL_BDX_PLAYER,                      -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BDX_PLAYER,                      -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BDX_PLAYER,                      -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BDX_PLAYER,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BDX_PLAYER,                      -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BDX_PLAYER,                      -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BDX_SAND_1,                      -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BDX_SAND_1
677   },
678
679   {
680     EL_BDX_ROCK,                        -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BDX_ROCK_FALLING
683   },
684   {
685     EL_BDX_ROCK,                        -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BDX_ROCK
688   },
689
690   {
691     EL_BDX_DIAMOND,                     -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BDX_DIAMOND,                     -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BDX_DIAMOND_FALLING
699   },
700   {
701     EL_BDX_DIAMOND,                     -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BDX_DIAMOND
704   },
705
706   {
707     EL_BDX_FIREFLY_1,                   -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_1_explodes_to,       EL_BDX_EXPLODING_1
710   },
711
712   {
713     EL_BDX_FIREFLY_2,                   -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BDX_EXPLODING_1
716   },
717
718   {
719     EL_BDX_BUTTERFLY_1,                 -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_1_explodes_to,     EL_BDX_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BDX_BUTTERFLY_2,                 -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BDX_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BDX_STONEFLY,                    -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BDX_ROCK_GROWING_1
734   },
735
736   {
737     EL_BDX_DRAGONFLY,                   -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BDX_EXPLODING_1
740   },
741
742   {
743     EL_BDX_DIAMOND_GROWING_5,   -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BDX_DIAMOND
746   },
747
748   {
749     EL_BDX_BOMB_EXPLODING_4,            -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BDX_WALL
752   },
753
754   {
755     EL_BDX_NITRO_PACK_EXPLODING_4,      -1,
756     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
757     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
758   },
759
760   {
761     EL_BDX_EXPLODING_5,                 -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BDX_MAGIC_WALL,                  -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BDX_MAGIC_WALL,                  -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BDX_MAGIC_WALL,                  -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BDX_MAGIC_WALL,                  -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BDX_MAGIC_WALL,                  -1,
788     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_time,             999
790   },
791   {
792     EL_BDX_MAGIC_WALL,                  -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_diamond_to,       EL_BDX_ROCK_FALLING
795   },
796   {
797     EL_BDX_MAGIC_WALL,                  -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_rock_to,          EL_BDX_DIAMOND_FALLING
800   },
801   {
802     EL_BDX_MAGIC_WALL,                  -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_mega_rock_to,     EL_BDX_NITRO_PACK_FALLING
805   },
806   {
807     EL_BDX_MAGIC_WALL,                  -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nut_to,           EL_BDX_NUT_FALLING
810   },
811   {
812     EL_BDX_MAGIC_WALL,                  -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_nitro_pack_to,    EL_BDX_MEGA_ROCK_FALLING
815   },
816   {
817     EL_BDX_MAGIC_WALL,                  -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_diamond_to, EL_BDX_FLYING_ROCK_FLYING
820   },
821   {
822     EL_BDX_MAGIC_WALL,                  -1,
823     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
824     &li.bd_magic_wall_flying_rock_to,   EL_BDX_FLYING_DIAMOND_FLYING
825   },
826
827   {
828     EL_BDX_CLOCK,                       -1,
829     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
830     &li.bd_clock_extra_time,            30
831   },
832
833   {
834     EL_BDX_VOODOO_DOLL,                 -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
836     &li.bd_voodoo_collects_diamonds,    FALSE
837   },
838   {
839     EL_BDX_VOODOO_DOLL,                 -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
841     &li.bd_voodoo_hurt_kills_player,    FALSE
842   },
843   {
844     EL_BDX_VOODOO_DOLL,                 -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
846     &li.bd_voodoo_dies_by_rock,         FALSE
847   },
848   {
849     EL_BDX_VOODOO_DOLL,                 -1,
850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
851     &li.bd_voodoo_vanish_by_explosion,  TRUE
852   },
853   {
854     EL_BDX_VOODOO_DOLL,                 -1,
855     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
856     &li.bd_voodoo_penalty_time,         30
857   },
858
859   {
860     EL_BDX_SLIME,                       -1,
861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
862     &li.bd_slime_is_predictable,        TRUE
863   },
864   {
865     EL_BDX_SLIME,                       -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
867     &li.bd_slime_permeability_rate,     100
868   },
869   {
870     EL_BDX_SLIME,                       -1,
871     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
872     &li.bd_slime_permeability_bits_c64, 0
873   },
874   {
875     EL_BDX_SLIME,                       -1,
876     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
877     &li.bd_slime_random_seed_c64,       -1
878   },
879   {
880     EL_BDX_SLIME,                       -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
882     &li.bd_slime_eats_element_1,        EL_BDX_DIAMOND
883   },
884   {
885     EL_BDX_SLIME,                       -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
887     &li.bd_slime_converts_to_element_1, EL_BDX_DIAMOND_FALLING
888   },
889   {
890     EL_BDX_SLIME,                       -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
892     &li.bd_slime_eats_element_2,        EL_BDX_ROCK
893   },
894   {
895     EL_BDX_SLIME,                       -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
897     &li.bd_slime_converts_to_element_2, EL_BDX_ROCK_FALLING
898   },
899   {
900     EL_BDX_SLIME,                       -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
902     &li.bd_slime_eats_element_3,        EL_BDX_NUT
903   },
904   {
905     EL_BDX_SLIME,                       -1,
906     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
907     &li.bd_slime_converts_to_element_3, EL_BDX_NUT_FALLING
908   },
909
910   {
911     EL_BDX_ACID,                        -1,
912     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
913     &li.bd_acid_eats_element,           EL_BDX_SAND_1
914   },
915   {
916     EL_BDX_ACID,                        -1,
917     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
918     &li.bd_acid_spread_rate,            3
919   },
920   {
921     EL_BDX_ACID,                        -1,
922     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
923     &li.bd_acid_turns_to_element,       EL_BDX_EXPLODING_3
924   },
925
926   {
927     EL_BDX_BITER,                       -1,
928     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
929     &li.bd_biter_move_delay,            0
930   },
931   {
932     EL_BDX_BITER,                       -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_biter_eats_element,          EL_BDX_DIAMOND
935   },
936
937   {
938     EL_BDX_BLADDER,                     -1,
939     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
940     &li.bd_bladder_converts_by_element, EL_BDX_VOODOO_DOLL
941   },
942
943   {
944     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
945     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
946     &li.bd_change_expanding_wall,       FALSE
947   },
948   {
949     EL_BDX_EXPANDABLE_WALL_ANY,         -1,
950     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
951     &li.bd_expanding_wall_looks_like,   EL_BDX_WALL
952   },
953
954   {
955     EL_BDX_REPLICATOR,                  -1,
956     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
957     &li.bd_replicators_active,          TRUE
958   },
959   {
960     EL_BDX_REPLICATOR,                  -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
962     &li.bd_replicator_create_delay,     4
963   },
964
965   {
966     EL_BDX_CONVEYOR_LEFT,               -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
968     &li.bd_conveyor_belts_active,       TRUE
969   },
970   {
971     EL_BDX_CONVEYOR_LEFT,               -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
973     &li.bd_conveyor_belts_changed,      FALSE
974   },
975
976   {
977     EL_BDX_WATER,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.bd_water_cannot_flow_down,      FALSE
980   },
981
982   {
983     EL_BDX_NUT,                         -1,
984     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
985     &li.bd_nut_content,                 EL_BDX_NUT_BREAKING_1
986   },
987
988   {
989     EL_BDX_PNEUMATIC_HAMMER,            -1,
990     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
991     &li.bd_hammer_walls_break_delay,    5
992   },
993   {
994     EL_BDX_PNEUMATIC_HAMMER,            -1,
995     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
996     &li.bd_hammer_walls_reappear,       FALSE
997   },
998   {
999     EL_BDX_PNEUMATIC_HAMMER,            -1,
1000     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1001     &li.bd_hammer_walls_reappear_delay, 100
1002   },
1003
1004   {
1005     EL_BDX_ROCKET_LAUNCHER,             -1,
1006     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1007     &li.bd_infinite_rockets,            FALSE
1008   },
1009
1010   {
1011     EL_BDX_SKELETON,                    -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1013     &li.bd_num_skeletons_needed_for_pot, 5
1014   },
1015   {
1016     EL_BDX_SKELETON,                    -1,
1017     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1018     &li.bd_skeleton_worth_num_diamonds, 0
1019   },
1020
1021   {
1022     EL_BDX_CREATURE_SWITCH,             -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1024     &li.bd_creatures_start_backwards,   FALSE
1025   },
1026   {
1027     EL_BDX_CREATURE_SWITCH,             -1,
1028     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1029     &li.bd_creatures_turn_on_hatching,  FALSE
1030   },
1031   {
1032     EL_BDX_CREATURE_SWITCH,             -1,
1033     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1034     &li.bd_creatures_auto_turn_delay,   0
1035   },
1036
1037   {
1038     EL_BDX_GRAVITY_SWITCH,              -1,
1039     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1040     &li.bd_gravity_direction,           GD_MV_DOWN
1041   },
1042   {
1043     EL_BDX_GRAVITY_SWITCH,              -1,
1044     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1045     &li.bd_gravity_switch_active,       FALSE
1046   },
1047   {
1048     EL_BDX_GRAVITY_SWITCH,              -1,
1049     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1050     &li.bd_gravity_switch_delay,        10
1051   },
1052   {
1053     EL_BDX_GRAVITY_SWITCH,              -1,
1054     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1055     &li.bd_gravity_affects_all,         TRUE
1056   },
1057
1058   // (the following values are related to various game elements)
1059
1060   {
1061     EL_EMERALD,                         -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_EMERALD],              10
1064   },
1065
1066   {
1067     EL_DIAMOND,                         -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_DIAMOND],              10
1070   },
1071
1072   {
1073     EL_BUG,                             -1,
1074     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1075     &li.score[SC_BUG],                  10
1076   },
1077
1078   {
1079     EL_SPACESHIP,                       -1,
1080     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1081     &li.score[SC_SPACESHIP],            10
1082   },
1083
1084   {
1085     EL_PACMAN,                          -1,
1086     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1087     &li.score[SC_PACMAN],               10
1088   },
1089
1090   {
1091     EL_NUT,                             -1,
1092     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1093     &li.score[SC_NUT],                  10
1094   },
1095
1096   {
1097     EL_DYNAMITE,                        -1,
1098     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1099     &li.score[SC_DYNAMITE],             10
1100   },
1101
1102   {
1103     EL_KEY_1,                           -1,
1104     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1105     &li.score[SC_KEY],                  10
1106   },
1107
1108   {
1109     EL_PEARL,                           -1,
1110     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1111     &li.score[SC_PEARL],                10
1112   },
1113
1114   {
1115     EL_CRYSTAL,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.score[SC_CRYSTAL],              10
1118   },
1119
1120   {
1121     EL_BD_AMOEBA,                       -1,
1122     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1123     &li.amoeba_content,                 EL_DIAMOND
1124   },
1125   {
1126     EL_BD_AMOEBA,                       -1,
1127     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1128     &li.amoeba_speed,                   10
1129   },
1130   {
1131     EL_BD_AMOEBA,                       -1,
1132     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1133     &li.grow_into_diggable,             TRUE
1134   },
1135
1136   {
1137     EL_BDX_AMOEBA_1,                    -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.bd_amoeba_1_threshold_too_big,  200
1140   },
1141   {
1142     EL_BDX_AMOEBA_1,                    -1,
1143     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1144     &li.bd_amoeba_1_slow_growth_time,   200
1145   },
1146   {
1147     EL_BDX_AMOEBA_1,                    -1,
1148     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1149     &li.bd_amoeba_1_content_too_big,    EL_BDX_ROCK
1150   },
1151   {
1152     EL_BDX_AMOEBA_1,                    -1,
1153     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1154     &li.bd_amoeba_1_content_enclosed,   EL_BDX_DIAMOND
1155   },
1156   {
1157     EL_BDX_AMOEBA_1,                    -1,
1158     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1159     &li.bd_amoeba_1_slow_growth_rate,   3
1160   },
1161   {
1162     EL_BDX_AMOEBA_1,                    -1,
1163     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1164     &li.bd_amoeba_1_fast_growth_rate,   25
1165   },
1166   {
1167     EL_BDX_AMOEBA_1,                    -1,
1168     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1169     &li.bd_amoeba_wait_for_hatching,    FALSE
1170   },
1171   {
1172     EL_BDX_AMOEBA_1,                    -1,
1173     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1174     &li.bd_amoeba_start_immediately,    TRUE
1175   },
1176
1177   {
1178     EL_BDX_AMOEBA_2,                    -1,
1179     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1180     &li.bd_amoeba_2_threshold_too_big,  200
1181   },
1182   {
1183     EL_BDX_AMOEBA_2,                    -1,
1184     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1185     &li.bd_amoeba_2_slow_growth_time,   200
1186   },
1187   {
1188     EL_BDX_AMOEBA_2,                    -1,
1189     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1190     &li.bd_amoeba_2_content_too_big,    EL_BDX_ROCK
1191   },
1192   {
1193     EL_BDX_AMOEBA_2,                    -1,
1194     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
1195     &li.bd_amoeba_2_content_enclosed,   EL_BDX_DIAMOND
1196   },
1197   {
1198     EL_BDX_AMOEBA_2,                    -1,
1199     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1200     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1201   },
1202   {
1203     EL_BDX_AMOEBA_2,                    -1,
1204     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1205     &li.bd_amoeba_2_content_looks_like, EL_BDX_AMOEBA_2
1206   },
1207   {
1208     EL_BDX_AMOEBA_2,                    -1,
1209     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1210     &li.bd_amoeba_2_slow_growth_rate,   3
1211   },
1212   {
1213     EL_BDX_AMOEBA_2,                    -1,
1214     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1215     &li.bd_amoeba_2_fast_growth_rate,   25
1216   },
1217   {
1218     EL_BDX_AMOEBA_2,                    -1,
1219     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1220     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1221   },
1222
1223   {
1224     EL_YAMYAM,                          -1,
1225     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.yamyam_content,                 EL_ROCK, NULL,
1227     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1228   },
1229   {
1230     EL_YAMYAM,                          -1,
1231     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1232     &li.score[SC_YAMYAM],               10
1233   },
1234
1235   {
1236     EL_ROBOT,                           -1,
1237     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1238     &li.score[SC_ROBOT],                10
1239   },
1240   {
1241     EL_ROBOT,                           -1,
1242     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1243     &li.slurp_score,                    10
1244   },
1245
1246   {
1247     EL_ROBOT_WHEEL,                     -1,
1248     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1249     &li.time_wheel,                     10
1250   },
1251
1252   {
1253     EL_MAGIC_WALL,                      -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1255     &li.time_magic_wall,                10
1256   },
1257
1258   {
1259     EL_GAME_OF_LIFE,                    -1,
1260     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1261     &li.game_of_life[0],                2
1262   },
1263   {
1264     EL_GAME_OF_LIFE,                    -1,
1265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1266     &li.game_of_life[1],                3
1267   },
1268   {
1269     EL_GAME_OF_LIFE,                    -1,
1270     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1271     &li.game_of_life[2],                3
1272   },
1273   {
1274     EL_GAME_OF_LIFE,                    -1,
1275     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1276     &li.game_of_life[3],                3
1277   },
1278   {
1279     EL_GAME_OF_LIFE,                    -1,
1280     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1281     &li.use_life_bugs,                  FALSE
1282   },
1283
1284   {
1285     EL_BIOMAZE,                         -1,
1286     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1287     &li.biomaze[0],                     2
1288   },
1289   {
1290     EL_BIOMAZE,                         -1,
1291     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1292     &li.biomaze[1],                     3
1293   },
1294   {
1295     EL_BIOMAZE,                         -1,
1296     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1297     &li.biomaze[2],                     3
1298   },
1299   {
1300     EL_BIOMAZE,                         -1,
1301     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1302     &li.biomaze[3],                     3
1303   },
1304
1305   {
1306     EL_TIMEGATE_SWITCH,                 -1,
1307     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1308     &li.time_timegate,                  10
1309   },
1310
1311   {
1312     EL_LIGHT_SWITCH_ACTIVE,             -1,
1313     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1314     &li.time_light,                     10
1315   },
1316
1317   {
1318     EL_SHIELD_NORMAL,                   -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.shield_normal_time,             10
1321   },
1322   {
1323     EL_SHIELD_NORMAL,                   -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.score[SC_SHIELD],               10
1326   },
1327
1328   {
1329     EL_SHIELD_DEADLY,                   -1,
1330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1331     &li.shield_deadly_time,             10
1332   },
1333   {
1334     EL_SHIELD_DEADLY,                   -1,
1335     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1336     &li.score[SC_SHIELD],               10
1337   },
1338
1339   {
1340     EL_EXTRA_TIME,                      -1,
1341     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1342     &li.extra_time,                     10
1343   },
1344   {
1345     EL_EXTRA_TIME,                      -1,
1346     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1347     &li.extra_time_score,               10
1348   },
1349
1350   {
1351     EL_TIME_ORB_FULL,                   -1,
1352     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1353     &li.time_orb_time,                  10
1354   },
1355   {
1356     EL_TIME_ORB_FULL,                   -1,
1357     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1358     &li.use_time_orb_bug,               FALSE
1359   },
1360
1361   {
1362     EL_SPRING,                          -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1364     &li.use_spring_bug,                 FALSE
1365   },
1366
1367   {
1368     EL_EMC_ANDROID,                     -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.android_move_time,              10
1371   },
1372   {
1373     EL_EMC_ANDROID,                     -1,
1374     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1375     &li.android_clone_time,             10
1376   },
1377   {
1378     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1379     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.android_clone_element[0],       EL_EMPTY, NULL,
1381     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1382   },
1383   {
1384     EL_EMC_ANDROID,                     -1,
1385     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1386     &li.android_clone_element[0],       EL_EMPTY, NULL,
1387     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1388   },
1389
1390   {
1391     EL_EMC_LENSES,                      -1,
1392     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1393     &li.lenses_score,                   10
1394   },
1395   {
1396     EL_EMC_LENSES,                      -1,
1397     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1398     &li.lenses_time,                    10
1399   },
1400
1401   {
1402     EL_EMC_MAGNIFIER,                   -1,
1403     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1404     &li.magnify_score,                  10
1405   },
1406   {
1407     EL_EMC_MAGNIFIER,                   -1,
1408     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1409     &li.magnify_time,                   10
1410   },
1411
1412   {
1413     EL_EMC_MAGIC_BALL,                  -1,
1414     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1415     &li.ball_time,                      10
1416   },
1417   {
1418     EL_EMC_MAGIC_BALL,                  -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1420     &li.ball_random,                    FALSE
1421   },
1422   {
1423     EL_EMC_MAGIC_BALL,                  -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1425     &li.ball_active_initial,            FALSE
1426   },
1427   {
1428     EL_EMC_MAGIC_BALL,                  -1,
1429     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1430     &li.ball_content,                   EL_EMPTY, NULL,
1431     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1432   },
1433
1434   {
1435     EL_SOKOBAN_FIELD_EMPTY,             -1,
1436     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1437     &li.sb_fields_needed,               TRUE
1438   },
1439
1440   {
1441     EL_SOKOBAN_OBJECT,                  -1,
1442     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1443     &li.sb_objects_needed,              TRUE
1444   },
1445
1446   {
1447     EL_MM_MCDUFFIN,                     -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &li.mm_laser_red,                   FALSE
1450   },
1451   {
1452     EL_MM_MCDUFFIN,                     -1,
1453     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1454     &li.mm_laser_green,                 FALSE
1455   },
1456   {
1457     EL_MM_MCDUFFIN,                     -1,
1458     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1459     &li.mm_laser_blue,                  TRUE
1460   },
1461
1462   {
1463     EL_DF_LASER,                        -1,
1464     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1465     &li.df_laser_red,                   TRUE
1466   },
1467   {
1468     EL_DF_LASER,                        -1,
1469     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1470     &li.df_laser_green,                 TRUE
1471   },
1472   {
1473     EL_DF_LASER,                        -1,
1474     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1475     &li.df_laser_blue,                  FALSE
1476   },
1477
1478   {
1479     EL_MM_FUSE_ACTIVE,                  -1,
1480     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1481     &li.mm_time_fuse,                   25
1482   },
1483   {
1484     EL_MM_BOMB,                         -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1486     &li.mm_time_bomb,                   75
1487   },
1488
1489   {
1490     EL_MM_GRAY_BALL,                    -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1492     &li.mm_time_ball,                   75
1493   },
1494   {
1495     EL_MM_GRAY_BALL,                    -1,
1496     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1497     &li.mm_ball_choice_mode,            ANIM_RANDOM
1498   },
1499   {
1500     EL_MM_GRAY_BALL,                    -1,
1501     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1502     &li.mm_ball_content,                EL_EMPTY, NULL,
1503     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1504   },
1505   {
1506     EL_MM_GRAY_BALL,                    -1,
1507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1508     &li.rotate_mm_ball_content,         TRUE
1509   },
1510   {
1511     EL_MM_GRAY_BALL,                    -1,
1512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1513     &li.explode_mm_ball,                FALSE
1514   },
1515
1516   {
1517     EL_MM_STEEL_BLOCK,                  -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1519     &li.mm_time_block,                  75
1520   },
1521   {
1522     EL_MM_LIGHTBALL,                    -1,
1523     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1524     &li.score[SC_ELEM_BONUS],           10
1525   },
1526
1527   {
1528     -1,                                 -1,
1529     -1,                                 -1,
1530     NULL,                               -1
1531   }
1532 };
1533
1534 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1535 {
1536   {
1537     -1,                                 -1,
1538     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1539     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1544     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1550     &xx_envelope.autowrap,              FALSE
1551   },
1552   {
1553     -1,                                 -1,
1554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1555     &xx_envelope.centered,              FALSE
1556   },
1557
1558   {
1559     -1,                                 -1,
1560     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1561     &xx_envelope.text,                  -1, NULL,
1562     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1563     &xx_default_string_empty[0]
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     -1,                                 -1,
1569     NULL,                               -1
1570   }
1571 };
1572
1573 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1574 {
1575   {
1576     -1,                                 -1,
1577     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1578     &xx_ei.description[0],              -1,
1579     &yy_ei.description[0],
1580     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1581     &xx_default_description[0]
1582   },
1583
1584   {
1585     -1,                                 -1,
1586     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1587     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1588     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1589   },
1590 #if ENABLE_RESERVED_CODE
1591   // (reserved for later use)
1592   {
1593     -1,                                 -1,
1594     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1595     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1596     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1597   },
1598 #endif
1599
1600   {
1601     -1,                                 -1,
1602     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1603     &xx_ei.use_gfx_element,             FALSE,
1604     &yy_ei.use_gfx_element
1605   },
1606   {
1607     -1,                                 -1,
1608     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1609     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1610     &yy_ei.gfx_element_initial
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1616     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1617     &yy_ei.access_direction
1618   },
1619
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1623     &xx_ei.collect_score_initial,       10,
1624     &yy_ei.collect_score_initial
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1629     &xx_ei.collect_count_initial,       1,
1630     &yy_ei.collect_count_initial
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1636     &xx_ei.ce_value_fixed_initial,      0,
1637     &yy_ei.ce_value_fixed_initial
1638   },
1639   {
1640     -1,                                 -1,
1641     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1642     &xx_ei.ce_value_random_initial,     0,
1643     &yy_ei.ce_value_random_initial
1644   },
1645   {
1646     -1,                                 -1,
1647     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1648     &xx_ei.use_last_ce_value,           FALSE,
1649     &yy_ei.use_last_ce_value
1650   },
1651
1652   {
1653     -1,                                 -1,
1654     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1655     &xx_ei.push_delay_fixed,            8,
1656     &yy_ei.push_delay_fixed
1657   },
1658   {
1659     -1,                                 -1,
1660     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1661     &xx_ei.push_delay_random,           8,
1662     &yy_ei.push_delay_random
1663   },
1664   {
1665     -1,                                 -1,
1666     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1667     &xx_ei.drop_delay_fixed,            0,
1668     &yy_ei.drop_delay_fixed
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1673     &xx_ei.drop_delay_random,           0,
1674     &yy_ei.drop_delay_random
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1679     &xx_ei.move_delay_fixed,            0,
1680     &yy_ei.move_delay_fixed
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1685     &xx_ei.move_delay_random,           0,
1686     &yy_ei.move_delay_random
1687   },
1688   {
1689     -1,                                 -1,
1690     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1691     &xx_ei.step_delay_fixed,            0,
1692     &yy_ei.step_delay_fixed
1693   },
1694   {
1695     -1,                                 -1,
1696     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1697     &xx_ei.step_delay_random,           0,
1698     &yy_ei.step_delay_random
1699   },
1700
1701   {
1702     -1,                                 -1,
1703     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1704     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1705     &yy_ei.move_pattern
1706   },
1707   {
1708     -1,                                 -1,
1709     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1710     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1711     &yy_ei.move_direction_initial
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1716     &xx_ei.move_stepsize,               TILEX / 8,
1717     &yy_ei.move_stepsize
1718   },
1719
1720   {
1721     -1,                                 -1,
1722     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1723     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1724     &yy_ei.move_enter_element
1725   },
1726   {
1727     -1,                                 -1,
1728     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1729     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1730     &yy_ei.move_leave_element
1731   },
1732   {
1733     -1,                                 -1,
1734     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1735     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1736     &yy_ei.move_leave_type
1737   },
1738
1739   {
1740     -1,                                 -1,
1741     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1742     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1743     &yy_ei.slippery_type
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1749     &xx_ei.explosion_type,              EXPLODES_3X3,
1750     &yy_ei.explosion_type
1751   },
1752   {
1753     -1,                                 -1,
1754     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1755     &xx_ei.explosion_delay,             16,
1756     &yy_ei.explosion_delay
1757   },
1758   {
1759     -1,                                 -1,
1760     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1761     &xx_ei.ignition_delay,              8,
1762     &yy_ei.ignition_delay
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1768     &xx_ei.content,                     EL_EMPTY_SPACE,
1769     &yy_ei.content,
1770     &xx_num_contents,                   1, 1
1771   },
1772
1773   // ---------- "num_change_pages" must be the last entry ---------------------
1774
1775   {
1776     -1,                                 SAVE_CONF_ALWAYS,
1777     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1778     &xx_ei.num_change_pages,            1,
1779     &yy_ei.num_change_pages
1780   },
1781
1782   {
1783     -1,                                 -1,
1784     -1,                                 -1,
1785     NULL,                               -1,
1786     NULL
1787   }
1788 };
1789
1790 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1791 {
1792   // ---------- "current_change_page" must be the first entry -----------------
1793
1794   {
1795     -1,                                 SAVE_CONF_ALWAYS,
1796     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1797     &xx_current_change_page,            -1
1798   },
1799
1800   // ---------- (the remaining entries can be in any order) -------------------
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1805     &xx_change.can_change,              FALSE
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1811     &xx_event_bits[0],                  0
1812   },
1813   {
1814     -1,                                 -1,
1815     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1816     &xx_event_bits[1],                  0
1817   },
1818
1819   {
1820     -1,                                 -1,
1821     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1822     &xx_change.trigger_player,          CH_PLAYER_ANY
1823   },
1824   {
1825     -1,                                 -1,
1826     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1827     &xx_change.trigger_side,            CH_SIDE_ANY
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1832     &xx_change.trigger_page,            CH_PAGE_ANY
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1838     &xx_change.target_element,          EL_EMPTY_SPACE
1839   },
1840
1841   {
1842     -1,                                 -1,
1843     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1844     &xx_change.delay_fixed,             0
1845   },
1846   {
1847     -1,                                 -1,
1848     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1849     &xx_change.delay_random,            0
1850   },
1851   {
1852     -1,                                 -1,
1853     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1854     &xx_change.delay_frames,            FRAMES_PER_SECOND
1855   },
1856
1857   {
1858     -1,                                 -1,
1859     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1860     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1861   },
1862
1863   {
1864     -1,                                 -1,
1865     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1866     &xx_change.explode,                 FALSE
1867   },
1868   {
1869     -1,                                 -1,
1870     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1871     &xx_change.use_target_content,      FALSE
1872   },
1873   {
1874     -1,                                 -1,
1875     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1876     &xx_change.only_if_complete,        FALSE
1877   },
1878   {
1879     -1,                                 -1,
1880     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1881     &xx_change.use_random_replace,      FALSE
1882   },
1883   {
1884     -1,                                 -1,
1885     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1886     &xx_change.random_percentage,       100
1887   },
1888   {
1889     -1,                                 -1,
1890     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1891     &xx_change.replace_when,            CP_WHEN_EMPTY
1892   },
1893
1894   {
1895     -1,                                 -1,
1896     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1897     &xx_change.has_action,              FALSE
1898   },
1899   {
1900     -1,                                 -1,
1901     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1902     &xx_change.action_type,             CA_NO_ACTION
1903   },
1904   {
1905     -1,                                 -1,
1906     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1907     &xx_change.action_mode,             CA_MODE_UNDEFINED
1908   },
1909   {
1910     -1,                                 -1,
1911     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1912     &xx_change.action_arg,              CA_ARG_UNDEFINED
1913   },
1914
1915   {
1916     -1,                                 -1,
1917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1918     &xx_change.action_element,          EL_EMPTY_SPACE
1919   },
1920
1921   {
1922     -1,                                 -1,
1923     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1924     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1925     &xx_num_contents,                   1, 1
1926   },
1927
1928   {
1929     -1,                                 -1,
1930     -1,                                 -1,
1931     NULL,                               -1
1932   }
1933 };
1934
1935 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1936 {
1937   {
1938     -1,                                 -1,
1939     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1940     &xx_ei.description[0],              -1, NULL,
1941     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1942     &xx_default_description[0]
1943   },
1944
1945   {
1946     -1,                                 -1,
1947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1948     &xx_ei.use_gfx_element,             FALSE
1949   },
1950   {
1951     -1,                                 -1,
1952     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1953     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1954   },
1955
1956   {
1957     -1,                                 -1,
1958     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1959     &xx_group.choice_mode,              ANIM_RANDOM
1960   },
1961
1962   {
1963     -1,                                 -1,
1964     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1965     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1966     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1967   },
1968
1969   {
1970     -1,                                 -1,
1971     -1,                                 -1,
1972     NULL,                               -1
1973   }
1974 };
1975
1976 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1977 {
1978   {
1979     -1,                                 -1,
1980     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1981     &xx_ei.use_gfx_element,             FALSE
1982   },
1983   {
1984     -1,                                 -1,
1985     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1986     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1987   },
1988
1989   {
1990     -1,                                 -1,
1991     -1,                                 -1,
1992     NULL,                               -1
1993   }
1994 };
1995
1996 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1997 {
1998   {
1999     EL_PLAYER_1,                        -1,
2000     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
2001     &li.block_snap_field,               TRUE
2002   },
2003   {
2004     EL_PLAYER_1,                        -1,
2005     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2006     &li.continuous_snapping,            TRUE
2007   },
2008   {
2009     EL_PLAYER_1,                        -1,
2010     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2011     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2012   },
2013   {
2014     EL_PLAYER_1,                        -1,
2015     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2016     &li.use_start_element[0],           FALSE
2017   },
2018   {
2019     EL_PLAYER_1,                        -1,
2020     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2021     &li.start_element[0],               EL_PLAYER_1
2022   },
2023   {
2024     EL_PLAYER_1,                        -1,
2025     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2026     &li.use_artwork_element[0],         FALSE
2027   },
2028   {
2029     EL_PLAYER_1,                        -1,
2030     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2031     &li.artwork_element[0],             EL_PLAYER_1
2032   },
2033   {
2034     EL_PLAYER_1,                        -1,
2035     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2036     &li.use_explosion_element[0],       FALSE
2037   },
2038   {
2039     EL_PLAYER_1,                        -1,
2040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2041     &li.explosion_element[0],           EL_PLAYER_1
2042   },
2043
2044   {
2045     -1,                                 -1,
2046     -1,                                 -1,
2047     NULL,                               -1
2048   }
2049 };
2050
2051 static struct
2052 {
2053   int filetype;
2054   char *id;
2055 }
2056 filetype_id_list[] =
2057 {
2058   { LEVEL_FILE_TYPE_RND,        "RND"   },
2059   { LEVEL_FILE_TYPE_BD,         "BD"    },
2060   { LEVEL_FILE_TYPE_EM,         "EM"    },
2061   { LEVEL_FILE_TYPE_SP,         "SP"    },
2062   { LEVEL_FILE_TYPE_DX,         "DX"    },
2063   { LEVEL_FILE_TYPE_SB,         "SB"    },
2064   { LEVEL_FILE_TYPE_DC,         "DC"    },
2065   { LEVEL_FILE_TYPE_MM,         "MM"    },
2066   { LEVEL_FILE_TYPE_MM,         "DF"    },
2067   { -1,                         NULL    },
2068 };
2069
2070
2071 // ============================================================================
2072 // level file functions
2073 // ============================================================================
2074
2075 static boolean check_special_flags(char *flag)
2076 {
2077   if (strEqual(options.special_flags, flag) ||
2078       strEqual(leveldir_current->special_flags, flag))
2079     return TRUE;
2080
2081   return FALSE;
2082 }
2083
2084 static struct DateInfo getCurrentDate(void)
2085 {
2086   time_t epoch_seconds = time(NULL);
2087   struct tm *now = localtime(&epoch_seconds);
2088   struct DateInfo date;
2089
2090   date.year  = now->tm_year + 1900;
2091   date.month = now->tm_mon  + 1;
2092   date.day   = now->tm_mday;
2093
2094   date.src   = DATE_SRC_CLOCK;
2095
2096   return date;
2097 }
2098
2099 static void resetEventFlags(struct ElementChangeInfo *change)
2100 {
2101   int i;
2102
2103   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2104     change->has_event[i] = FALSE;
2105 }
2106
2107 static void resetEventBits(void)
2108 {
2109   int i;
2110
2111   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2112     xx_event_bits[i] = 0;
2113 }
2114
2115 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2116 {
2117   int i;
2118
2119   /* important: only change event flag if corresponding event bit is set
2120      (this is because all xx_event_bits[] values are loaded separately,
2121      and all xx_event_bits[] values are set back to zero before loading
2122      another value xx_event_bits[x] (each value representing 32 flags)) */
2123
2124   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2125     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2126       change->has_event[i] = TRUE;
2127 }
2128
2129 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2130 {
2131   int i;
2132
2133   /* in contrast to the above function setEventFlagsFromEventBits(), it
2134      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2135      depending on the corresponding change->has_event[i] values here, as
2136      all xx_event_bits[] values are reset in resetEventBits() before */
2137
2138   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2139     if (change->has_event[i])
2140       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2141 }
2142
2143 static char *getDefaultElementDescription(struct ElementInfo *ei)
2144 {
2145   static char description[MAX_ELEMENT_NAME_LEN + 1];
2146   char *default_description = (ei->custom_description != NULL ?
2147                                ei->custom_description :
2148                                ei->editor_description);
2149   int i;
2150
2151   // always start with reliable default values
2152   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2153     description[i] = '\0';
2154
2155   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2156   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2157
2158   return &description[0];
2159 }
2160
2161 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2162 {
2163   char *default_description = getDefaultElementDescription(ei);
2164   int i;
2165
2166   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2167     ei->description[i] = default_description[i];
2168 }
2169
2170 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2171 {
2172   int i;
2173
2174   for (i = 0; conf[i].data_type != -1; i++)
2175   {
2176     int default_value = conf[i].default_value;
2177     int data_type = conf[i].data_type;
2178     int conf_type = conf[i].conf_type;
2179     int byte_mask = conf_type & CONF_MASK_BYTES;
2180
2181     if (byte_mask == CONF_MASK_MULTI_BYTES)
2182     {
2183       int default_num_entities = conf[i].default_num_entities;
2184       int max_num_entities = conf[i].max_num_entities;
2185
2186       *(int *)(conf[i].num_entities) = default_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *default_string = conf[i].default_string;
2191         char *string = (char *)(conf[i].value);
2192
2193         strncpy(string, default_string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array = (int *)(conf[i].value);
2198         int j;
2199
2200         for (j = 0; j < max_num_entities; j++)
2201           element_array[j] = default_value;
2202       }
2203       else if (data_type == TYPE_CONTENT_LIST)
2204       {
2205         struct Content *content = (struct Content *)(conf[i].value);
2206         int c, x, y;
2207
2208         for (c = 0; c < max_num_entities; c++)
2209           for (y = 0; y < 3; y++)
2210             for (x = 0; x < 3; x++)
2211               content[c].e[x][y] = default_value;
2212       }
2213     }
2214     else        // constant size configuration data (1, 2 or 4 bytes)
2215     {
2216       if (data_type == TYPE_BOOLEAN)
2217         *(boolean *)(conf[i].value) = default_value;
2218       else
2219         *(int *)    (conf[i].value) = default_value;
2220     }
2221   }
2222 }
2223
2224 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2225 {
2226   int i;
2227
2228   for (i = 0; conf[i].data_type != -1; i++)
2229   {
2230     int data_type = conf[i].data_type;
2231     int conf_type = conf[i].conf_type;
2232     int byte_mask = conf_type & CONF_MASK_BYTES;
2233
2234     if (byte_mask == CONF_MASK_MULTI_BYTES)
2235     {
2236       int max_num_entities = conf[i].max_num_entities;
2237
2238       if (data_type == TYPE_STRING)
2239       {
2240         char *string      = (char *)(conf[i].value);
2241         char *string_copy = (char *)(conf[i].value_copy);
2242
2243         strncpy(string_copy, string, max_num_entities);
2244       }
2245       else if (data_type == TYPE_ELEMENT_LIST)
2246       {
2247         int *element_array      = (int *)(conf[i].value);
2248         int *element_array_copy = (int *)(conf[i].value_copy);
2249         int j;
2250
2251         for (j = 0; j < max_num_entities; j++)
2252           element_array_copy[j] = element_array[j];
2253       }
2254       else if (data_type == TYPE_CONTENT_LIST)
2255       {
2256         struct Content *content      = (struct Content *)(conf[i].value);
2257         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2258         int c, x, y;
2259
2260         for (c = 0; c < max_num_entities; c++)
2261           for (y = 0; y < 3; y++)
2262             for (x = 0; x < 3; x++)
2263               content_copy[c].e[x][y] = content[c].e[x][y];
2264       }
2265     }
2266     else        // constant size configuration data (1, 2 or 4 bytes)
2267     {
2268       if (data_type == TYPE_BOOLEAN)
2269         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2270       else
2271         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2272     }
2273   }
2274 }
2275
2276 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2277 {
2278   int i;
2279
2280   xx_ei = *ei_from;     // copy element data into temporary buffer
2281   yy_ei = *ei_to;       // copy element data into temporary buffer
2282
2283   copyConfigFromConfigList(chunk_config_CUSX_base);
2284
2285   *ei_from = xx_ei;
2286   *ei_to   = yy_ei;
2287
2288   // ---------- reinitialize and copy change pages ----------
2289
2290   ei_to->num_change_pages = ei_from->num_change_pages;
2291   ei_to->current_change_page = ei_from->current_change_page;
2292
2293   setElementChangePages(ei_to, ei_to->num_change_pages);
2294
2295   for (i = 0; i < ei_to->num_change_pages; i++)
2296     ei_to->change_page[i] = ei_from->change_page[i];
2297
2298   // ---------- copy group element info ----------
2299   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2300     *ei_to->group = *ei_from->group;
2301
2302   // mark this custom element as modified
2303   ei_to->modified_settings = TRUE;
2304 }
2305
2306 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2307 {
2308   int change_page_size = sizeof(struct ElementChangeInfo);
2309
2310   ei->num_change_pages = MAX(1, change_pages);
2311
2312   ei->change_page =
2313     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2314
2315   if (ei->current_change_page >= ei->num_change_pages)
2316     ei->current_change_page = ei->num_change_pages - 1;
2317
2318   ei->change = &ei->change_page[ei->current_change_page];
2319 }
2320
2321 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2322 {
2323   xx_change = *change;          // copy change data into temporary buffer
2324
2325   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2326
2327   *change = xx_change;
2328
2329   resetEventFlags(change);
2330
2331   change->direct_action = 0;
2332   change->other_action = 0;
2333
2334   change->pre_change_function = NULL;
2335   change->change_function = NULL;
2336   change->post_change_function = NULL;
2337 }
2338
2339 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2340 {
2341   boolean add_border = FALSE;
2342   int x1 = 0;
2343   int y1 = 0;
2344   int x2 = STD_LEV_FIELDX - 1;
2345   int y2 = STD_LEV_FIELDY - 1;
2346   int i, x, y;
2347
2348   li = *level;          // copy level data into temporary buffer
2349   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2350   *level = li;          // copy temporary buffer back to level data
2351
2352   setLevelInfoToDefaults_BD();
2353   setLevelInfoToDefaults_EM();
2354   setLevelInfoToDefaults_SP();
2355   setLevelInfoToDefaults_MM();
2356
2357   level->native_bd_level = &native_bd_level;
2358   level->native_em_level = &native_em_level;
2359   level->native_sp_level = &native_sp_level;
2360   level->native_mm_level = &native_mm_level;
2361
2362   level->file_version = FILE_VERSION_ACTUAL;
2363   level->game_version = GAME_VERSION_ACTUAL;
2364
2365   level->creation_date = getCurrentDate();
2366
2367   level->encoding_16bit_field  = TRUE;
2368   level->encoding_16bit_yamyam = TRUE;
2369   level->encoding_16bit_amoeba = TRUE;
2370
2371   // clear level name and level author string buffers
2372   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2373     level->name[i] = '\0';
2374   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2375     level->author[i] = '\0';
2376
2377   // set level name and level author to default values
2378   strcpy(level->name, NAMELESS_LEVEL_NAME);
2379   strcpy(level->author, ANONYMOUS_NAME);
2380
2381   // set default game engine type
2382   level->game_engine_type = setup.default_game_engine_type;
2383
2384   // some game engines should have a default playfield with border elements
2385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD ||
2386       level->game_engine_type == GAME_ENGINE_TYPE_EM ||
2387       level->game_engine_type == GAME_ENGINE_TYPE_SP)
2388   {
2389     add_border = TRUE;
2390     x1++;
2391     y1++;
2392     x2--;
2393     y2--;
2394   }
2395
2396   // set level playfield to playable default level with player and exit
2397   for (x = 0; x < MAX_LEV_FIELDX; x++)
2398   {
2399     for (y = 0; y < MAX_LEV_FIELDY; y++)
2400     {
2401       if (add_border && (x == 0 || x == STD_LEV_FIELDX - 1 ||
2402                          y == 0 || y == STD_LEV_FIELDY - 1))
2403         level->field[x][y] = getEngineElement(EL_STEELWALL);
2404       else
2405         level->field[x][y] = getEngineElement(EL_SAND);
2406     }
2407   }
2408
2409   level->field[x1][y1] = getEngineElement(EL_PLAYER_1);
2410   level->field[x2][y2] = getEngineElement(EL_EXIT_CLOSED);
2411
2412   BorderElement = getEngineElement(EL_STEELWALL);
2413
2414   // detect custom elements when loading them
2415   level->file_has_custom_elements = FALSE;
2416
2417   // set random colors for BD style levels according to preferred color type
2418   SetRandomLevelColors_BD(setup.bd_default_color_type);
2419
2420   // set default color type and colors for BD style level colors
2421   SetDefaultLevelColorType_BD();
2422   SetDefaultLevelColors_BD();
2423
2424   // set all bug compatibility flags to "false" => do not emulate this bug
2425   level->use_action_after_change_bug = FALSE;
2426
2427   if (leveldir_current)
2428   {
2429     // try to determine better author name than 'anonymous'
2430     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2431     {
2432       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2433       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2434     }
2435     else
2436     {
2437       switch (LEVELCLASS(leveldir_current))
2438       {
2439         case LEVELCLASS_TUTORIAL:
2440           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2441           break;
2442
2443         case LEVELCLASS_CONTRIB:
2444           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2445           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2446           break;
2447
2448         case LEVELCLASS_PRIVATE:
2449           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2450           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2451           break;
2452
2453         default:
2454           // keep default value
2455           break;
2456       }
2457     }
2458   }
2459 }
2460
2461 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2462 {
2463   static boolean clipboard_elements_initialized = FALSE;
2464   int i;
2465
2466   InitElementPropertiesStatic();
2467
2468   li = *level;          // copy level data into temporary buffer
2469   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2470   *level = li;          // copy temporary buffer back to level data
2471
2472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2473   {
2474     int element = i;
2475     struct ElementInfo *ei = &element_info[element];
2476
2477     if (element == EL_MM_GRAY_BALL)
2478     {
2479       struct LevelInfo_MM *level_mm = level->native_mm_level;
2480       int j;
2481
2482       for (j = 0; j < level->num_mm_ball_contents; j++)
2483         level->mm_ball_content[j] =
2484           map_element_MM_to_RND(level_mm->ball_content[j]);
2485     }
2486
2487     // never initialize clipboard elements after the very first time
2488     // (to be able to use clipboard elements between several levels)
2489     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2490       continue;
2491
2492     if (IS_ENVELOPE(element))
2493     {
2494       int envelope_nr = element - EL_ENVELOPE_1;
2495
2496       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2497
2498       level->envelope[envelope_nr] = xx_envelope;
2499     }
2500
2501     if (IS_CUSTOM_ELEMENT(element) ||
2502         IS_GROUP_ELEMENT(element) ||
2503         IS_INTERNAL_ELEMENT(element))
2504     {
2505       xx_ei = *ei;      // copy element data into temporary buffer
2506
2507       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2508
2509       *ei = xx_ei;
2510     }
2511
2512     setElementChangePages(ei, 1);
2513     setElementChangeInfoToDefaults(ei->change);
2514
2515     if (IS_CUSTOM_ELEMENT(element) ||
2516         IS_GROUP_ELEMENT(element))
2517     {
2518       setElementDescriptionToDefault(ei);
2519
2520       ei->modified_settings = FALSE;
2521     }
2522
2523     if (IS_CUSTOM_ELEMENT(element) ||
2524         IS_INTERNAL_ELEMENT(element))
2525     {
2526       // internal values used in level editor
2527
2528       ei->access_type = 0;
2529       ei->access_layer = 0;
2530       ei->access_protected = 0;
2531       ei->walk_to_action = 0;
2532       ei->smash_targets = 0;
2533       ei->deadliness = 0;
2534
2535       ei->can_explode_by_fire = FALSE;
2536       ei->can_explode_smashed = FALSE;
2537       ei->can_explode_impact = FALSE;
2538
2539       ei->current_change_page = 0;
2540     }
2541
2542     if (IS_GROUP_ELEMENT(element) ||
2543         IS_INTERNAL_ELEMENT(element))
2544     {
2545       struct ElementGroupInfo *group;
2546
2547       // initialize memory for list of elements in group
2548       if (ei->group == NULL)
2549         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2550
2551       group = ei->group;
2552
2553       xx_group = *group;        // copy group data into temporary buffer
2554
2555       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2556
2557       *group = xx_group;
2558     }
2559
2560     if (IS_EMPTY_ELEMENT(element) ||
2561         IS_INTERNAL_ELEMENT(element))
2562     {
2563       xx_ei = *ei;              // copy element data into temporary buffer
2564
2565       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2566
2567       *ei = xx_ei;
2568     }
2569   }
2570
2571   clipboard_elements_initialized = TRUE;
2572 }
2573
2574 static void setLevelInfoToDefaults(struct LevelInfo *level,
2575                                    boolean level_info_only,
2576                                    boolean reset_file_status)
2577 {
2578   setLevelInfoToDefaults_Level(level);
2579
2580   if (!level_info_only)
2581     setLevelInfoToDefaults_Elements(level);
2582
2583   if (reset_file_status)
2584   {
2585     level->no_valid_file = FALSE;
2586     level->no_level_file = FALSE;
2587   }
2588
2589   level->changed = FALSE;
2590 }
2591
2592 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2593 {
2594   level_file_info->nr = 0;
2595   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2596   level_file_info->packed = FALSE;
2597
2598   setString(&level_file_info->basename, NULL);
2599   setString(&level_file_info->filename, NULL);
2600 }
2601
2602 int getMappedElement_SB(int, boolean);
2603
2604 static void ActivateLevelTemplate(void)
2605 {
2606   int x, y;
2607
2608   if (check_special_flags("load_xsb_to_ces"))
2609   {
2610     // fill smaller playfields with padding "beyond border wall" elements
2611     if (level.fieldx < level_template.fieldx ||
2612         level.fieldy < level_template.fieldy)
2613     {
2614       short field[level.fieldx][level.fieldy];
2615       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2616       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2617       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2618       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2619
2620       // copy old playfield (which is smaller than the visible area)
2621       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2622         field[x][y] = level.field[x][y];
2623
2624       // fill new, larger playfield with "beyond border wall" elements
2625       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2626         level.field[x][y] = getMappedElement_SB('_', TRUE);
2627
2628       // copy the old playfield to the middle of the new playfield
2629       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2630         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2631
2632       level.fieldx = new_fieldx;
2633       level.fieldy = new_fieldy;
2634     }
2635   }
2636
2637   // Currently there is no special action needed to activate the template
2638   // data, because 'element_info' property settings overwrite the original
2639   // level data, while all other variables do not change.
2640
2641   // Exception: 'from_level_template' elements in the original level playfield
2642   // are overwritten with the corresponding elements at the same position in
2643   // playfield from the level template.
2644
2645   for (x = 0; x < level.fieldx; x++)
2646     for (y = 0; y < level.fieldy; y++)
2647       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2648         level.field[x][y] = level_template.field[x][y];
2649
2650   if (check_special_flags("load_xsb_to_ces"))
2651   {
2652     struct LevelInfo level_backup = level;
2653
2654     // overwrite all individual level settings from template level settings
2655     level = level_template;
2656
2657     // restore level file info
2658     level.file_info = level_backup.file_info;
2659
2660     // restore playfield size
2661     level.fieldx = level_backup.fieldx;
2662     level.fieldy = level_backup.fieldy;
2663
2664     // restore playfield content
2665     for (x = 0; x < level.fieldx; x++)
2666       for (y = 0; y < level.fieldy; y++)
2667         level.field[x][y] = level_backup.field[x][y];
2668
2669     // restore name and author from individual level
2670     strcpy(level.name,   level_backup.name);
2671     strcpy(level.author, level_backup.author);
2672
2673     // restore flag "use_custom_template"
2674     level.use_custom_template = level_backup.use_custom_template;
2675   }
2676 }
2677
2678 boolean isLevelsetFilename_BD(char *filename)
2679 {
2680   return (strSuffixLower(filename, ".bd") ||
2681           strSuffixLower(filename, ".bdr") ||
2682           strSuffixLower(filename, ".brc") ||
2683           strSuffixLower(filename, ".gds"));
2684 }
2685
2686 static boolean checkForPackageFromBasename_BD(char *basename)
2687 {
2688   // check for native BD level file extensions
2689   if (!isLevelsetFilename_BD(basename))
2690     return FALSE;
2691
2692   // check for standard single-level BD files (like "001.bd")
2693   if (strSuffixLower(basename, ".bd") &&
2694       strlen(basename) == 6 &&
2695       basename[0] >= '0' && basename[0] <= '9' &&
2696       basename[1] >= '0' && basename[1] <= '9' &&
2697       basename[2] >= '0' && basename[2] <= '9')
2698     return FALSE;
2699
2700   // this is a level package in native BD file format
2701   return TRUE;
2702 }
2703
2704 static char *getLevelFilenameFromBasename(char *basename)
2705 {
2706   static char *filename = NULL;
2707
2708   checked_free(filename);
2709
2710   filename = getPath2(getCurrentLevelDir(), basename);
2711
2712   return filename;
2713 }
2714
2715 static int getFileTypeFromBasename(char *basename)
2716 {
2717   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2718
2719   static char *filename = NULL;
2720   struct stat file_status;
2721
2722   // ---------- try to determine file type from filename ----------
2723
2724   // check for typical filename of a Supaplex level package file
2725   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2726     return LEVEL_FILE_TYPE_SP;
2727
2728   // check for typical filename of a Diamond Caves II level package file
2729   if (strSuffixLower(basename, ".dc") ||
2730       strSuffixLower(basename, ".dc2"))
2731     return LEVEL_FILE_TYPE_DC;
2732
2733   // check for typical filename of a Sokoban level package file
2734   if (strSuffixLower(basename, ".xsb") &&
2735       strchr(basename, '%') == NULL)
2736     return LEVEL_FILE_TYPE_SB;
2737
2738   // check for typical filename of a Boulder Dash (GDash) level package file
2739   if (checkForPackageFromBasename_BD(basename))
2740     return LEVEL_FILE_TYPE_BD;
2741
2742   // ---------- try to determine file type from filesize ----------
2743
2744   checked_free(filename);
2745   filename = getPath2(getCurrentLevelDir(), basename);
2746
2747   if (stat(filename, &file_status) == 0)
2748   {
2749     // check for typical filesize of a Supaplex level package file
2750     if (file_status.st_size == 170496)
2751       return LEVEL_FILE_TYPE_SP;
2752   }
2753
2754   return LEVEL_FILE_TYPE_UNKNOWN;
2755 }
2756
2757 static int getFileTypeFromMagicBytes(char *filename, int type)
2758 {
2759   File *file;
2760
2761   if ((file = openFile(filename, MODE_READ)))
2762   {
2763     char chunk_name[CHUNK_ID_LEN + 1];
2764
2765     getFileChunkBE(file, chunk_name, NULL);
2766
2767     if (strEqual(chunk_name, "MMII") ||
2768         strEqual(chunk_name, "MIRR"))
2769       type = LEVEL_FILE_TYPE_MM;
2770
2771     closeFile(file);
2772   }
2773
2774   return type;
2775 }
2776
2777 static boolean checkForPackageFromBasename(char *basename)
2778 {
2779   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2780   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2781
2782   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2783 }
2784
2785 static char *getSingleLevelBasenameExt(int nr, char *extension)
2786 {
2787   static char basename[MAX_FILENAME_LEN];
2788
2789   if (nr < 0)
2790     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2791   else
2792     sprintf(basename, "%03d.%s", nr, extension);
2793
2794   return basename;
2795 }
2796
2797 static char *getSingleLevelBasename(int nr)
2798 {
2799   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2800 }
2801
2802 static char *getPackedLevelBasename(int type)
2803 {
2804   static char basename[MAX_FILENAME_LEN];
2805   char *directory = getCurrentLevelDir();
2806   Directory *dir;
2807   DirectoryEntry *dir_entry;
2808
2809   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2810
2811   if ((dir = openDirectory(directory)) == NULL)
2812   {
2813     Warn("cannot read current level directory '%s'", directory);
2814
2815     return basename;
2816   }
2817
2818   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2819   {
2820     char *entry_basename = dir_entry->basename;
2821     int entry_type = getFileTypeFromBasename(entry_basename);
2822
2823     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2824     {
2825       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2826           type == entry_type)
2827       {
2828         strcpy(basename, entry_basename);
2829
2830         break;
2831       }
2832     }
2833   }
2834
2835   closeDirectory(dir);
2836
2837   return basename;
2838 }
2839
2840 static char *getSingleLevelFilename(int nr)
2841 {
2842   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2843 }
2844
2845 #if ENABLE_UNUSED_CODE
2846 static char *getPackedLevelFilename(int type)
2847 {
2848   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2849 }
2850 #endif
2851
2852 char *getDefaultLevelFilename(int nr)
2853 {
2854   return getSingleLevelFilename(nr);
2855 }
2856
2857 #if ENABLE_UNUSED_CODE
2858 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2859                                                  int type)
2860 {
2861   lfi->type = type;
2862   lfi->packed = FALSE;
2863
2864   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2865   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2866 }
2867 #endif
2868
2869 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2870                                                  int type, char *format, ...)
2871 {
2872   static char basename[MAX_FILENAME_LEN];
2873   va_list ap;
2874
2875   va_start(ap, format);
2876   vsprintf(basename, format, ap);
2877   va_end(ap);
2878
2879   lfi->type = type;
2880   lfi->packed = FALSE;
2881
2882   setString(&lfi->basename, basename);
2883   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2884 }
2885
2886 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2887                                                  int type)
2888 {
2889   lfi->type = type;
2890   lfi->packed = TRUE;
2891
2892   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2893   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2894 }
2895
2896 static int getFiletypeFromID(char *filetype_id)
2897 {
2898   char *filetype_id_lower;
2899   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2900   int i;
2901
2902   if (filetype_id == NULL)
2903     return LEVEL_FILE_TYPE_UNKNOWN;
2904
2905   filetype_id_lower = getStringToLower(filetype_id);
2906
2907   for (i = 0; filetype_id_list[i].id != NULL; i++)
2908   {
2909     char *id_lower = getStringToLower(filetype_id_list[i].id);
2910     
2911     if (strEqual(filetype_id_lower, id_lower))
2912       filetype = filetype_id_list[i].filetype;
2913
2914     free(id_lower);
2915
2916     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2917       break;
2918   }
2919
2920   free(filetype_id_lower);
2921
2922   return filetype;
2923 }
2924
2925 char *getLocalLevelTemplateFilename(void)
2926 {
2927   return getLevelFilenameFromBasename(LEVELTEMPLATE_FILENAME);
2928 }
2929
2930 char *getGlobalLevelTemplateFilename(void)
2931 {
2932   return getFilenameFromCurrentLevelDirUpward(LEVELTEMPLATE_FILENAME);
2933 }
2934
2935 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2936 {
2937   int nr = lfi->nr;
2938
2939   // special case: level number is negative => check for level template file
2940   if (nr < 0)
2941   {
2942     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2943                                          getSingleLevelBasename(-1));
2944
2945     // replace local level template filename with global template filename
2946     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2947
2948     // no fallback if template file not existing
2949     return;
2950   }
2951
2952   // special case: check for file name/pattern specified in "levelinfo.conf"
2953   if (leveldir_current->level_filename != NULL)
2954   {
2955     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2956
2957     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2958                                          leveldir_current->level_filename, nr);
2959
2960     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2961
2962     if (fileExists(lfi->filename))
2963       return;
2964   }
2965   else if (leveldir_current->level_filetype != NULL)
2966   {
2967     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2968
2969     // check for specified native level file with standard file name
2970     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2971                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2972     if (fileExists(lfi->filename))
2973       return;
2974   }
2975
2976   // check for native Rocks'n'Diamonds level file
2977   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2978                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2979   if (fileExists(lfi->filename))
2980     return;
2981
2982   // check for native Boulder Dash level file
2983   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2984   if (fileExists(lfi->filename))
2985     return;
2986
2987   // check for Emerald Mine level file (V1)
2988   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2989                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2990   if (fileExists(lfi->filename))
2991     return;
2992   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2993                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2994   if (fileExists(lfi->filename))
2995     return;
2996
2997   // check for Emerald Mine level file (V2 to V5)
2998   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2999   if (fileExists(lfi->filename))
3000     return;
3001
3002   // check for Emerald Mine level file (V6 / single mode)
3003   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
3004   if (fileExists(lfi->filename))
3005     return;
3006   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
3007   if (fileExists(lfi->filename))
3008     return;
3009
3010   // check for Emerald Mine level file (V6 / teamwork mode)
3011   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
3012   if (fileExists(lfi->filename))
3013     return;
3014   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3015   if (fileExists(lfi->filename))
3016     return;
3017
3018   // check for various packed level file formats
3019   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3020   if (fileExists(lfi->filename))
3021     return;
3022
3023   // no known level file found -- use default values (and fail later)
3024   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3025                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3026 }
3027
3028 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3029 {
3030   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3031     lfi->type = getFileTypeFromBasename(lfi->basename);
3032
3033   if (lfi->type == LEVEL_FILE_TYPE_RND)
3034     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3035 }
3036
3037 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3038 {
3039   // always start with reliable default values
3040   setFileInfoToDefaults(level_file_info);
3041
3042   level_file_info->nr = nr;     // set requested level number
3043
3044   determineLevelFileInfo_Filename(level_file_info);
3045   determineLevelFileInfo_Filetype(level_file_info);
3046 }
3047
3048 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3049                               struct LevelFileInfo *lfi_to)
3050 {
3051   lfi_to->nr     = lfi_from->nr;
3052   lfi_to->type   = lfi_from->type;
3053   lfi_to->packed = lfi_from->packed;
3054
3055   setString(&lfi_to->basename, lfi_from->basename);
3056   setString(&lfi_to->filename, lfi_from->filename);
3057 }
3058
3059 // ----------------------------------------------------------------------------
3060 // functions for loading R'n'D level
3061 // ----------------------------------------------------------------------------
3062
3063 int getMappedElement(int element)
3064 {
3065   // remap some (historic, now obsolete) elements
3066
3067   switch (element)
3068   {
3069     case EL_PLAYER_OBSOLETE:
3070       element = EL_PLAYER_1;
3071       break;
3072
3073     case EL_KEY_OBSOLETE:
3074       element = EL_KEY_1;
3075       break;
3076
3077     case EL_EM_KEY_1_FILE_OBSOLETE:
3078       element = EL_EM_KEY_1;
3079       break;
3080
3081     case EL_EM_KEY_2_FILE_OBSOLETE:
3082       element = EL_EM_KEY_2;
3083       break;
3084
3085     case EL_EM_KEY_3_FILE_OBSOLETE:
3086       element = EL_EM_KEY_3;
3087       break;
3088
3089     case EL_EM_KEY_4_FILE_OBSOLETE:
3090       element = EL_EM_KEY_4;
3091       break;
3092
3093     case EL_ENVELOPE_OBSOLETE:
3094       element = EL_ENVELOPE_1;
3095       break;
3096
3097     case EL_SP_EMPTY:
3098       element = EL_EMPTY;
3099       break;
3100
3101     default:
3102       if (element >= NUM_FILE_ELEMENTS)
3103       {
3104         Warn("invalid level element %d", element);
3105
3106         element = EL_UNKNOWN;
3107       }
3108       break;
3109   }
3110
3111   return element;
3112 }
3113
3114 static int getMappedElementByVersion(int element, int game_version)
3115 {
3116   // remap some elements due to certain game version
3117
3118   if (game_version <= VERSION_IDENT(2,2,0,0))
3119   {
3120     // map game font elements
3121     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3122                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3123                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3124                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3125   }
3126
3127   if (game_version < VERSION_IDENT(3,0,0,0))
3128   {
3129     // map Supaplex gravity tube elements
3130     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3131                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3132                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3133                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3134                element);
3135   }
3136
3137   return element;
3138 }
3139
3140 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3141 {
3142   level->file_version = getFileVersion(file);
3143   level->game_version = getFileVersion(file);
3144
3145   return chunk_size;
3146 }
3147
3148 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3149 {
3150   level->creation_date.year  = getFile16BitBE(file);
3151   level->creation_date.month = getFile8Bit(file);
3152   level->creation_date.day   = getFile8Bit(file);
3153
3154   level->creation_date.src   = DATE_SRC_LEVELFILE;
3155
3156   return chunk_size;
3157 }
3158
3159 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3160 {
3161   int initial_player_stepsize;
3162   int initial_player_gravity;
3163   int i, x, y;
3164
3165   level->fieldx = getFile8Bit(file);
3166   level->fieldy = getFile8Bit(file);
3167
3168   level->time           = getFile16BitBE(file);
3169   level->gems_needed    = getFile16BitBE(file);
3170
3171   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3172     level->name[i] = getFile8Bit(file);
3173   level->name[MAX_LEVEL_NAME_LEN] = 0;
3174
3175   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3176     level->score[i] = getFile8Bit(file);
3177
3178   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3179   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3180     for (y = 0; y < 3; y++)
3181       for (x = 0; x < 3; x++)
3182         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3183
3184   level->amoeba_speed           = getFile8Bit(file);
3185   level->time_magic_wall        = getFile8Bit(file);
3186   level->time_wheel             = getFile8Bit(file);
3187   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3188
3189   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3190                                    STEPSIZE_NORMAL);
3191
3192   for (i = 0; i < MAX_PLAYERS; i++)
3193     level->initial_player_stepsize[i] = initial_player_stepsize;
3194
3195   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196
3197   for (i = 0; i < MAX_PLAYERS; i++)
3198     level->initial_player_gravity[i] = initial_player_gravity;
3199
3200   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3201   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3202
3203   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3204
3205   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3206   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3207   level->can_move_into_acid_bits = getFile32BitBE(file);
3208   level->dont_collide_with_bits = getFile8Bit(file);
3209
3210   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3211   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3212
3213   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3214   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3215   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3216
3217   level->game_engine_type       = getFile8Bit(file);
3218
3219   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3220
3221   return chunk_size;
3222 }
3223
3224 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3225 {
3226   int i;
3227
3228   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3229     level->name[i] = getFile8Bit(file);
3230   level->name[MAX_LEVEL_NAME_LEN] = 0;
3231
3232   return chunk_size;
3233 }
3234
3235 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3236 {
3237   int i;
3238
3239   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3240     level->author[i] = getFile8Bit(file);
3241   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3242
3243   return chunk_size;
3244 }
3245
3246 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3247 {
3248   int x, y;
3249   int chunk_size_expected = level->fieldx * level->fieldy;
3250
3251   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3252      stored with 16-bit encoding (and should be twice as big then).
3253      Even worse, playfield data was stored 16-bit when only yamyam content
3254      contained 16-bit elements and vice versa. */
3255
3256   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3257     chunk_size_expected *= 2;
3258
3259   if (chunk_size_expected != chunk_size)
3260   {
3261     ReadUnusedBytesFromFile(file, chunk_size);
3262     return chunk_size_expected;
3263   }
3264
3265   for (y = 0; y < level->fieldy; y++)
3266     for (x = 0; x < level->fieldx; x++)
3267       level->field[x][y] =
3268         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3269                          getFile8Bit(file));
3270   return chunk_size;
3271 }
3272
3273 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3274 {
3275   int i, x, y;
3276   int header_size = 4;
3277   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3278   int chunk_size_expected = header_size + content_size;
3279
3280   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3281      stored with 16-bit encoding (and should be twice as big then).
3282      Even worse, playfield data was stored 16-bit when only yamyam content
3283      contained 16-bit elements and vice versa. */
3284
3285   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3286     chunk_size_expected += content_size;
3287
3288   if (chunk_size_expected != chunk_size)
3289   {
3290     ReadUnusedBytesFromFile(file, chunk_size);
3291     return chunk_size_expected;
3292   }
3293
3294   getFile8Bit(file);
3295   level->num_yamyam_contents = getFile8Bit(file);
3296   getFile8Bit(file);
3297   getFile8Bit(file);
3298
3299   // correct invalid number of content fields -- should never happen
3300   if (level->num_yamyam_contents < 1 ||
3301       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3302     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3303
3304   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3305     for (y = 0; y < 3; y++)
3306       for (x = 0; x < 3; x++)
3307         level->yamyam_content[i].e[x][y] =
3308           getMappedElement(level->encoding_16bit_field ?
3309                            getFile16BitBE(file) : getFile8Bit(file));
3310   return chunk_size;
3311 }
3312
3313 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3314 {
3315   int i, x, y;
3316   int element;
3317   int num_contents;
3318   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3319
3320   element = getMappedElement(getFile16BitBE(file));
3321   num_contents = getFile8Bit(file);
3322
3323   getFile8Bit(file);    // content x size (unused)
3324   getFile8Bit(file);    // content y size (unused)
3325
3326   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3327
3328   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3329     for (y = 0; y < 3; y++)
3330       for (x = 0; x < 3; x++)
3331         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3332
3333   // correct invalid number of content fields -- should never happen
3334   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3335     num_contents = STD_ELEMENT_CONTENTS;
3336
3337   if (element == EL_YAMYAM)
3338   {
3339     level->num_yamyam_contents = num_contents;
3340
3341     for (i = 0; i < num_contents; i++)
3342       for (y = 0; y < 3; y++)
3343         for (x = 0; x < 3; x++)
3344           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3345   }
3346   else if (element == EL_BD_AMOEBA)
3347   {
3348     level->amoeba_content = content_array[0][0][0];
3349   }
3350   else
3351   {
3352     Warn("cannot load content for element '%d'", element);
3353   }
3354
3355   return chunk_size;
3356 }
3357
3358 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3359 {
3360   int i;
3361   int element;
3362   int envelope_nr;
3363   int envelope_len;
3364   int chunk_size_expected;
3365
3366   element = getMappedElement(getFile16BitBE(file));
3367   if (!IS_ENVELOPE(element))
3368     element = EL_ENVELOPE_1;
3369
3370   envelope_nr = element - EL_ENVELOPE_1;
3371
3372   envelope_len = getFile16BitBE(file);
3373
3374   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3375   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3376
3377   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3378
3379   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3380   if (chunk_size_expected != chunk_size)
3381   {
3382     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3383     return chunk_size_expected;
3384   }
3385
3386   for (i = 0; i < envelope_len; i++)
3387     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3388
3389   return chunk_size;
3390 }
3391
3392 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3393 {
3394   int num_changed_custom_elements = getFile16BitBE(file);
3395   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3396   int i;
3397
3398   if (chunk_size_expected != chunk_size)
3399   {
3400     ReadUnusedBytesFromFile(file, chunk_size - 2);
3401     return chunk_size_expected;
3402   }
3403
3404   for (i = 0; i < num_changed_custom_elements; i++)
3405   {
3406     int element = getMappedElement(getFile16BitBE(file));
3407     int properties = getFile32BitBE(file);
3408
3409     if (IS_CUSTOM_ELEMENT(element))
3410       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3411     else
3412       Warn("invalid custom element number %d", element);
3413
3414     // older game versions that wrote level files with CUS1 chunks used
3415     // different default push delay values (not yet stored in level file)
3416     element_info[element].push_delay_fixed = 2;
3417     element_info[element].push_delay_random = 8;
3418   }
3419
3420   level->file_has_custom_elements = TRUE;
3421
3422   return chunk_size;
3423 }
3424
3425 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3426 {
3427   int num_changed_custom_elements = getFile16BitBE(file);
3428   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3429   int i;
3430
3431   if (chunk_size_expected != chunk_size)
3432   {
3433     ReadUnusedBytesFromFile(file, chunk_size - 2);
3434     return chunk_size_expected;
3435   }
3436
3437   for (i = 0; i < num_changed_custom_elements; i++)
3438   {
3439     int element = getMappedElement(getFile16BitBE(file));
3440     int custom_target_element = getMappedElement(getFile16BitBE(file));
3441
3442     if (IS_CUSTOM_ELEMENT(element))
3443       element_info[element].change->target_element = custom_target_element;
3444     else
3445       Warn("invalid custom element number %d", element);
3446   }
3447
3448   level->file_has_custom_elements = TRUE;
3449
3450   return chunk_size;
3451 }
3452
3453 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3454 {
3455   int num_changed_custom_elements = getFile16BitBE(file);
3456   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3457   int i, j, x, y;
3458
3459   if (chunk_size_expected != chunk_size)
3460   {
3461     ReadUnusedBytesFromFile(file, chunk_size - 2);
3462     return chunk_size_expected;
3463   }
3464
3465   for (i = 0; i < num_changed_custom_elements; i++)
3466   {
3467     int element = getMappedElement(getFile16BitBE(file));
3468     struct ElementInfo *ei = &element_info[element];
3469     unsigned int event_bits;
3470
3471     if (!IS_CUSTOM_ELEMENT(element))
3472     {
3473       Warn("invalid custom element number %d", element);
3474
3475       element = EL_INTERNAL_DUMMY;
3476     }
3477
3478     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3479       ei->description[j] = getFile8Bit(file);
3480     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3481
3482     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3483
3484     // some free bytes for future properties and padding
3485     ReadUnusedBytesFromFile(file, 7);
3486
3487     ei->use_gfx_element = getFile8Bit(file);
3488     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3489
3490     ei->collect_score_initial = getFile8Bit(file);
3491     ei->collect_count_initial = getFile8Bit(file);
3492
3493     ei->push_delay_fixed = getFile16BitBE(file);
3494     ei->push_delay_random = getFile16BitBE(file);
3495     ei->move_delay_fixed = getFile16BitBE(file);
3496     ei->move_delay_random = getFile16BitBE(file);
3497
3498     ei->move_pattern = getFile16BitBE(file);
3499     ei->move_direction_initial = getFile8Bit(file);
3500     ei->move_stepsize = getFile8Bit(file);
3501
3502     for (y = 0; y < 3; y++)
3503       for (x = 0; x < 3; x++)
3504         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3505
3506     // bits 0 - 31 of "has_event[]"
3507     event_bits = getFile32BitBE(file);
3508     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3509       if (event_bits & (1u << j))
3510         ei->change->has_event[j] = TRUE;
3511
3512     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3513
3514     ei->change->delay_fixed = getFile16BitBE(file);
3515     ei->change->delay_random = getFile16BitBE(file);
3516     ei->change->delay_frames = getFile16BitBE(file);
3517
3518     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3519
3520     ei->change->explode = getFile8Bit(file);
3521     ei->change->use_target_content = getFile8Bit(file);
3522     ei->change->only_if_complete = getFile8Bit(file);
3523     ei->change->use_random_replace = getFile8Bit(file);
3524
3525     ei->change->random_percentage = getFile8Bit(file);
3526     ei->change->replace_when = getFile8Bit(file);
3527
3528     for (y = 0; y < 3; y++)
3529       for (x = 0; x < 3; x++)
3530         ei->change->target_content.e[x][y] =
3531           getMappedElement(getFile16BitBE(file));
3532
3533     ei->slippery_type = getFile8Bit(file);
3534
3535     // some free bytes for future properties and padding
3536     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3537
3538     // mark that this custom element has been modified
3539     ei->modified_settings = TRUE;
3540   }
3541
3542   level->file_has_custom_elements = TRUE;
3543
3544   return chunk_size;
3545 }
3546
3547 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3548 {
3549   struct ElementInfo *ei;
3550   int chunk_size_expected;
3551   int element;
3552   int i, j, x, y;
3553
3554   // ---------- custom element base property values (96 bytes) ----------------
3555
3556   element = getMappedElement(getFile16BitBE(file));
3557
3558   if (!IS_CUSTOM_ELEMENT(element))
3559   {
3560     Warn("invalid custom element number %d", element);
3561
3562     ReadUnusedBytesFromFile(file, chunk_size - 2);
3563
3564     return chunk_size;
3565   }
3566
3567   ei = &element_info[element];
3568
3569   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3570     ei->description[i] = getFile8Bit(file);
3571   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3572
3573   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3574
3575   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3576
3577   ei->num_change_pages = getFile8Bit(file);
3578
3579   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3580   if (chunk_size_expected != chunk_size)
3581   {
3582     ReadUnusedBytesFromFile(file, chunk_size - 43);
3583     return chunk_size_expected;
3584   }
3585
3586   ei->ce_value_fixed_initial = getFile16BitBE(file);
3587   ei->ce_value_random_initial = getFile16BitBE(file);
3588   ei->use_last_ce_value = getFile8Bit(file);
3589
3590   ei->use_gfx_element = getFile8Bit(file);
3591   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3592
3593   ei->collect_score_initial = getFile8Bit(file);
3594   ei->collect_count_initial = getFile8Bit(file);
3595
3596   ei->drop_delay_fixed = getFile8Bit(file);
3597   ei->push_delay_fixed = getFile8Bit(file);
3598   ei->drop_delay_random = getFile8Bit(file);
3599   ei->push_delay_random = getFile8Bit(file);
3600   ei->move_delay_fixed = getFile16BitBE(file);
3601   ei->move_delay_random = getFile16BitBE(file);
3602
3603   // bits 0 - 15 of "move_pattern" ...
3604   ei->move_pattern = getFile16BitBE(file);
3605   ei->move_direction_initial = getFile8Bit(file);
3606   ei->move_stepsize = getFile8Bit(file);
3607
3608   ei->slippery_type = getFile8Bit(file);
3609
3610   for (y = 0; y < 3; y++)
3611     for (x = 0; x < 3; x++)
3612       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3613
3614   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3615   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3616   ei->move_leave_type = getFile8Bit(file);
3617
3618   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3619   ei->move_pattern |= (getFile16BitBE(file) << 16);
3620
3621   ei->access_direction = getFile8Bit(file);
3622
3623   ei->explosion_delay = getFile8Bit(file);
3624   ei->ignition_delay = getFile8Bit(file);
3625   ei->explosion_type = getFile8Bit(file);
3626
3627   // some free bytes for future custom property values and padding
3628   ReadUnusedBytesFromFile(file, 1);
3629
3630   // ---------- change page property values (48 bytes) ------------------------
3631
3632   setElementChangePages(ei, ei->num_change_pages);
3633
3634   for (i = 0; i < ei->num_change_pages; i++)
3635   {
3636     struct ElementChangeInfo *change = &ei->change_page[i];
3637     unsigned int event_bits;
3638
3639     // always start with reliable default values
3640     setElementChangeInfoToDefaults(change);
3641
3642     // bits 0 - 31 of "has_event[]" ...
3643     event_bits = getFile32BitBE(file);
3644     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3645       if (event_bits & (1u << j))
3646         change->has_event[j] = TRUE;
3647
3648     change->target_element = getMappedElement(getFile16BitBE(file));
3649
3650     change->delay_fixed = getFile16BitBE(file);
3651     change->delay_random = getFile16BitBE(file);
3652     change->delay_frames = getFile16BitBE(file);
3653
3654     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3655
3656     change->explode = getFile8Bit(file);
3657     change->use_target_content = getFile8Bit(file);
3658     change->only_if_complete = getFile8Bit(file);
3659     change->use_random_replace = getFile8Bit(file);
3660
3661     change->random_percentage = getFile8Bit(file);
3662     change->replace_when = getFile8Bit(file);
3663
3664     for (y = 0; y < 3; y++)
3665       for (x = 0; x < 3; x++)
3666         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3667
3668     change->can_change = getFile8Bit(file);
3669
3670     change->trigger_side = getFile8Bit(file);
3671
3672     change->trigger_player = getFile8Bit(file);
3673     change->trigger_page = getFile8Bit(file);
3674
3675     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3676                             CH_PAGE_ANY : (1 << change->trigger_page));
3677
3678     change->has_action = getFile8Bit(file);
3679     change->action_type = getFile8Bit(file);
3680     change->action_mode = getFile8Bit(file);
3681     change->action_arg = getFile16BitBE(file);
3682
3683     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3684     event_bits = getFile8Bit(file);
3685     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3686       if (event_bits & (1u << (j - 32)))
3687         change->has_event[j] = TRUE;
3688   }
3689
3690   // mark this custom element as modified
3691   ei->modified_settings = TRUE;
3692
3693   level->file_has_custom_elements = TRUE;
3694
3695   return chunk_size;
3696 }
3697
3698 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3699 {
3700   struct ElementInfo *ei;
3701   struct ElementGroupInfo *group;
3702   int element;
3703   int i;
3704
3705   element = getMappedElement(getFile16BitBE(file));
3706
3707   if (!IS_GROUP_ELEMENT(element))
3708   {
3709     Warn("invalid group element number %d", element);
3710
3711     ReadUnusedBytesFromFile(file, chunk_size - 2);
3712
3713     return chunk_size;
3714   }
3715
3716   ei = &element_info[element];
3717
3718   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3719     ei->description[i] = getFile8Bit(file);
3720   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3721
3722   group = element_info[element].group;
3723
3724   group->num_elements = getFile8Bit(file);
3725
3726   ei->use_gfx_element = getFile8Bit(file);
3727   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3728
3729   group->choice_mode = getFile8Bit(file);
3730
3731   // some free bytes for future values and padding
3732   ReadUnusedBytesFromFile(file, 3);
3733
3734   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3735     group->element[i] = getMappedElement(getFile16BitBE(file));
3736
3737   // mark this group element as modified
3738   element_info[element].modified_settings = TRUE;
3739
3740   level->file_has_custom_elements = TRUE;
3741
3742   return chunk_size;
3743 }
3744
3745 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3746                                 int element, int real_element)
3747 {
3748   int micro_chunk_size = 0;
3749   int conf_type = getFile8Bit(file);
3750   int byte_mask = conf_type & CONF_MASK_BYTES;
3751   boolean element_found = FALSE;
3752   int i;
3753
3754   micro_chunk_size += 1;
3755
3756   if (byte_mask == CONF_MASK_MULTI_BYTES)
3757   {
3758     int num_bytes = getFile16BitBE(file);
3759     byte *buffer = checked_malloc(num_bytes);
3760
3761     ReadBytesFromFile(file, buffer, num_bytes);
3762
3763     for (i = 0; conf[i].data_type != -1; i++)
3764     {
3765       if (conf[i].element == element &&
3766           conf[i].conf_type == conf_type)
3767       {
3768         int data_type = conf[i].data_type;
3769         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3770         int max_num_entities = conf[i].max_num_entities;
3771
3772         if (num_entities > max_num_entities)
3773         {
3774           Warn("truncating number of entities for element %d from %d to %d",
3775                element, num_entities, max_num_entities);
3776
3777           num_entities = max_num_entities;
3778         }
3779
3780         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3781                                   data_type == TYPE_CONTENT_LIST))
3782         {
3783           // for element and content lists, zero entities are not allowed
3784           Warn("found empty list of entities for element %d", element);
3785
3786           // do not set "num_entities" here to prevent reading behind buffer
3787
3788           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3789         }
3790         else
3791         {
3792           *(int *)(conf[i].num_entities) = num_entities;
3793         }
3794
3795         element_found = TRUE;
3796
3797         if (data_type == TYPE_STRING)
3798         {
3799           char *string = (char *)(conf[i].value);
3800           int j;
3801
3802           for (j = 0; j < max_num_entities; j++)
3803             string[j] = (j < num_entities ? buffer[j] : '\0');
3804         }
3805         else if (data_type == TYPE_ELEMENT_LIST)
3806         {
3807           int *element_array = (int *)(conf[i].value);
3808           int j;
3809
3810           for (j = 0; j < num_entities; j++)
3811             element_array[j] =
3812               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3813         }
3814         else if (data_type == TYPE_CONTENT_LIST)
3815         {
3816           struct Content *content= (struct Content *)(conf[i].value);
3817           int c, x, y;
3818
3819           for (c = 0; c < num_entities; c++)
3820             for (y = 0; y < 3; y++)
3821               for (x = 0; x < 3; x++)
3822                 content[c].e[x][y] =
3823                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3824         }
3825         else
3826           element_found = FALSE;
3827
3828         break;
3829       }
3830     }
3831
3832     checked_free(buffer);
3833
3834     micro_chunk_size += 2 + num_bytes;
3835   }
3836   else          // constant size configuration data (1, 2 or 4 bytes)
3837   {
3838     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3839                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3840                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3841
3842     for (i = 0; conf[i].data_type != -1; i++)
3843     {
3844       if (conf[i].element == element &&
3845           conf[i].conf_type == conf_type)
3846       {
3847         int data_type = conf[i].data_type;
3848
3849         if (data_type == TYPE_ELEMENT)
3850           value = getMappedElement(value);
3851
3852         if (data_type == TYPE_BOOLEAN)
3853           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3854         else
3855           *(int *)    (conf[i].value) = value;
3856
3857         element_found = TRUE;
3858
3859         break;
3860       }
3861     }
3862
3863     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3864   }
3865
3866   if (!element_found)
3867   {
3868     char *error_conf_chunk_bytes =
3869       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3870        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3871        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3872     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3873     int error_element = real_element;
3874
3875     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3876          error_conf_chunk_bytes, error_conf_chunk_token,
3877          error_element, EL_NAME(error_element));
3878   }
3879
3880   return micro_chunk_size;
3881 }
3882
3883 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3884 {
3885   int real_chunk_size = 0;
3886
3887   li = *level;          // copy level data into temporary buffer
3888
3889   while (!checkEndOfFile(file))
3890   {
3891     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3892
3893     if (real_chunk_size >= chunk_size)
3894       break;
3895   }
3896
3897   *level = li;          // copy temporary buffer back to level data
3898
3899   return real_chunk_size;
3900 }
3901
3902 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3903 {
3904   int real_chunk_size = 0;
3905
3906   li = *level;          // copy level data into temporary buffer
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     int element = getMappedElement(getFile16BitBE(file));
3911
3912     real_chunk_size += 2;
3913     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3914                                             element, element);
3915     if (real_chunk_size >= chunk_size)
3916       break;
3917   }
3918
3919   *level = li;          // copy temporary buffer back to level data
3920
3921   return real_chunk_size;
3922 }
3923
3924 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3925 {
3926   int real_chunk_size = 0;
3927
3928   li = *level;          // copy level data into temporary buffer
3929
3930   while (!checkEndOfFile(file))
3931   {
3932     int element = getMappedElement(getFile16BitBE(file));
3933
3934     real_chunk_size += 2;
3935     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3936                                             element, element);
3937     if (real_chunk_size >= chunk_size)
3938       break;
3939   }
3940
3941   *level = li;          // copy temporary buffer back to level data
3942
3943   return real_chunk_size;
3944 }
3945
3946 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3947 {
3948   int element = getMappedElement(getFile16BitBE(file));
3949   int envelope_nr = element - EL_ENVELOPE_1;
3950   int real_chunk_size = 2;
3951
3952   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3953
3954   while (!checkEndOfFile(file))
3955   {
3956     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3957                                             -1, element);
3958
3959     if (real_chunk_size >= chunk_size)
3960       break;
3961   }
3962
3963   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3964
3965   return real_chunk_size;
3966 }
3967
3968 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3969 {
3970   int element = getMappedElement(getFile16BitBE(file));
3971   int real_chunk_size = 2;
3972   struct ElementInfo *ei = &element_info[element];
3973   int i;
3974
3975   xx_ei = *ei;          // copy element data into temporary buffer
3976
3977   xx_ei.num_change_pages = -1;
3978
3979   while (!checkEndOfFile(file))
3980   {
3981     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3982                                             -1, element);
3983     if (xx_ei.num_change_pages != -1)
3984       break;
3985
3986     if (real_chunk_size >= chunk_size)
3987       break;
3988   }
3989
3990   *ei = xx_ei;
3991
3992   if (ei->num_change_pages == -1)
3993   {
3994     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3995          EL_NAME(element));
3996
3997     ei->num_change_pages = 1;
3998
3999     setElementChangePages(ei, 1);
4000     setElementChangeInfoToDefaults(ei->change);
4001
4002     return real_chunk_size;
4003   }
4004
4005   // initialize number of change pages stored for this custom element
4006   setElementChangePages(ei, ei->num_change_pages);
4007   for (i = 0; i < ei->num_change_pages; i++)
4008     setElementChangeInfoToDefaults(&ei->change_page[i]);
4009
4010   // start with reading properties for the first change page
4011   xx_current_change_page = 0;
4012
4013   while (!checkEndOfFile(file))
4014   {
4015     // level file might contain invalid change page number
4016     if (xx_current_change_page >= ei->num_change_pages)
4017       break;
4018
4019     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4020
4021     xx_change = *change;        // copy change data into temporary buffer
4022
4023     resetEventBits();           // reset bits; change page might have changed
4024
4025     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4026                                             -1, element);
4027
4028     *change = xx_change;
4029
4030     setEventFlagsFromEventBits(change);
4031
4032     if (real_chunk_size >= chunk_size)
4033       break;
4034   }
4035
4036   level->file_has_custom_elements = TRUE;
4037
4038   return real_chunk_size;
4039 }
4040
4041 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4042 {
4043   int element = getMappedElement(getFile16BitBE(file));
4044   int real_chunk_size = 2;
4045   struct ElementInfo *ei = &element_info[element];
4046   struct ElementGroupInfo *group = ei->group;
4047
4048   if (group == NULL)
4049     return -1;
4050
4051   xx_ei = *ei;          // copy element data into temporary buffer
4052   xx_group = *group;    // copy group data into temporary buffer
4053
4054   while (!checkEndOfFile(file))
4055   {
4056     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4057                                             -1, element);
4058
4059     if (real_chunk_size >= chunk_size)
4060       break;
4061   }
4062
4063   *ei = xx_ei;
4064   *group = xx_group;
4065
4066   level->file_has_custom_elements = TRUE;
4067
4068   return real_chunk_size;
4069 }
4070
4071 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4072 {
4073   int element = getMappedElement(getFile16BitBE(file));
4074   int real_chunk_size = 2;
4075   struct ElementInfo *ei = &element_info[element];
4076
4077   xx_ei = *ei;          // copy element data into temporary buffer
4078
4079   while (!checkEndOfFile(file))
4080   {
4081     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4082                                             -1, element);
4083
4084     if (real_chunk_size >= chunk_size)
4085       break;
4086   }
4087
4088   *ei = xx_ei;
4089
4090   level->file_has_custom_elements = TRUE;
4091
4092   return real_chunk_size;
4093 }
4094
4095 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4096                                       struct LevelFileInfo *level_file_info,
4097                                       boolean level_info_only)
4098 {
4099   char *filename = level_file_info->filename;
4100   char cookie[MAX_LINE_LEN];
4101   char chunk_name[CHUNK_ID_LEN + 1];
4102   int chunk_size;
4103   File *file;
4104
4105   if (!(file = openFile(filename, MODE_READ)))
4106   {
4107     level->no_valid_file = TRUE;
4108     level->no_level_file = TRUE;
4109
4110     if (level_info_only)
4111       return;
4112
4113     Warn("cannot read level '%s' -- using empty level", filename);
4114
4115     if (!setup.editor.use_template_for_new_levels)
4116       return;
4117
4118     // if level file not found, try to initialize level data from template
4119     filename = getGlobalLevelTemplateFilename();
4120
4121     if (!(file = openFile(filename, MODE_READ)))
4122       return;
4123
4124     // default: for empty levels, use level template for custom elements
4125     level->use_custom_template = TRUE;
4126
4127     level->no_valid_file = FALSE;
4128   }
4129
4130   getFileChunkBE(file, chunk_name, NULL);
4131   if (strEqual(chunk_name, "RND1"))
4132   {
4133     getFile32BitBE(file);               // not used
4134
4135     getFileChunkBE(file, chunk_name, NULL);
4136     if (!strEqual(chunk_name, "CAVE"))
4137     {
4138       level->no_valid_file = TRUE;
4139
4140       Warn("unknown format of level file '%s'", filename);
4141
4142       closeFile(file);
4143
4144       return;
4145     }
4146   }
4147   else  // check for pre-2.0 file format with cookie string
4148   {
4149     strcpy(cookie, chunk_name);
4150     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4151       cookie[4] = '\0';
4152     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4153       cookie[strlen(cookie) - 1] = '\0';
4154
4155     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4156     {
4157       level->no_valid_file = TRUE;
4158
4159       Warn("unknown format of level file '%s'", filename);
4160
4161       closeFile(file);
4162
4163       return;
4164     }
4165
4166     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4167     {
4168       level->no_valid_file = TRUE;
4169
4170       Warn("unsupported version of level file '%s'", filename);
4171
4172       closeFile(file);
4173
4174       return;
4175     }
4176
4177     // pre-2.0 level files have no game version, so use file version here
4178     level->game_version = level->file_version;
4179   }
4180
4181   if (level->file_version < FILE_VERSION_1_2)
4182   {
4183     // level files from versions before 1.2.0 without chunk structure
4184     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4185     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4186   }
4187   else
4188   {
4189     static struct
4190     {
4191       char *name;
4192       int size;
4193       int (*loader)(File *, int, struct LevelInfo *);
4194     }
4195     chunk_info[] =
4196     {
4197       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4198       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4199       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4200       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4201       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4202       { "INFO", -1,                     LoadLevel_INFO },
4203       { "BODY", -1,                     LoadLevel_BODY },
4204       { "CONT", -1,                     LoadLevel_CONT },
4205       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4206       { "CNT3", -1,                     LoadLevel_CNT3 },
4207       { "CUS1", -1,                     LoadLevel_CUS1 },
4208       { "CUS2", -1,                     LoadLevel_CUS2 },
4209       { "CUS3", -1,                     LoadLevel_CUS3 },
4210       { "CUS4", -1,                     LoadLevel_CUS4 },
4211       { "GRP1", -1,                     LoadLevel_GRP1 },
4212       { "CONF", -1,                     LoadLevel_CONF },
4213       { "ELEM", -1,                     LoadLevel_ELEM },
4214       { "NOTE", -1,                     LoadLevel_NOTE },
4215       { "CUSX", -1,                     LoadLevel_CUSX },
4216       { "GRPX", -1,                     LoadLevel_GRPX },
4217       { "EMPX", -1,                     LoadLevel_EMPX },
4218
4219       {  NULL,  0,                      NULL }
4220     };
4221
4222     while (getFileChunkBE(file, chunk_name, &chunk_size))
4223     {
4224       int i = 0;
4225
4226       while (chunk_info[i].name != NULL &&
4227              !strEqual(chunk_name, chunk_info[i].name))
4228         i++;
4229
4230       if (chunk_info[i].name == NULL)
4231       {
4232         Warn("unknown chunk '%s' in level file '%s'",
4233              chunk_name, filename);
4234
4235         ReadUnusedBytesFromFile(file, chunk_size);
4236       }
4237       else if (chunk_info[i].size != -1 &&
4238                chunk_info[i].size != chunk_size)
4239       {
4240         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4241              chunk_size, chunk_name, filename);
4242
4243         ReadUnusedBytesFromFile(file, chunk_size);
4244       }
4245       else
4246       {
4247         // call function to load this level chunk
4248         int chunk_size_expected =
4249           (chunk_info[i].loader)(file, chunk_size, level);
4250
4251         if (chunk_size_expected < 0)
4252         {
4253           Warn("error reading chunk '%s' in level file '%s'",
4254                chunk_name, filename);
4255
4256           break;
4257         }
4258
4259         // the size of some chunks cannot be checked before reading other
4260         // chunks first (like "HEAD" and "BODY") that contain some header
4261         // information, so check them here
4262         if (chunk_size_expected != chunk_size)
4263         {
4264           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4265                chunk_size, chunk_name, filename);
4266
4267           break;
4268         }
4269       }
4270     }
4271   }
4272
4273   closeFile(file);
4274 }
4275
4276
4277 // ----------------------------------------------------------------------------
4278 // functions for loading BD level
4279 // ----------------------------------------------------------------------------
4280
4281 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4282 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4283
4284 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4285 {
4286   struct LevelInfo_BD *level_bd = level->native_bd_level;
4287   GdCave *cave = NULL;  // will be changed below
4288   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4289   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4290   int x, y;
4291
4292   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4293
4294   // cave and map newly allocated when set to defaults above
4295   cave = level_bd->cave;
4296
4297   // level type
4298   cave->intermission                    = level->bd_intermission;
4299
4300   // level settings
4301   cave->level_time[0]                   = level->time;
4302   cave->level_diamonds[0]               = level->gems_needed;
4303
4304   // game timing
4305   cave->scheduling                      = level->bd_scheduling_type;
4306   cave->pal_timing                      = level->bd_pal_timing;
4307   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4308   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4309   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4310   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4311
4312   // scores
4313   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4314   cave->diamond_value                   = level->score[SC_EMERALD];
4315   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4316
4317   // compatibility settings
4318   cave->lineshift                       = level->bd_line_shifting_borders;
4319   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4320   cave->short_explosions                = level->bd_short_explosions;
4321
4322   // player properties
4323   cave->diagonal_movements              = level->bd_diagonal_movements;
4324   cave->active_is_first_found           = level->bd_topmost_player_active;
4325   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4326   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4327   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4328   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4329
4330   // element properties
4331   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4332   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4333   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4334   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4335   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4336   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4337   cave->level_magic_wall_time[0]        = level->bd_magic_wall_time;
4338   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4339   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4340   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4341   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4342
4343   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4344   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4345   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4346   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4347   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4348   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4349   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4350
4351   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4352   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4353   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4354   cave->level_amoeba_threshold[0]       = level->bd_amoeba_1_threshold_too_big;
4355   cave->level_amoeba_time[0]            = level->bd_amoeba_1_slow_growth_time;
4356   cave->amoeba_growth_prob              = level->bd_amoeba_1_slow_growth_rate * 10000;
4357   cave->amoeba_fast_growth_prob         = level->bd_amoeba_1_fast_growth_rate * 10000;
4358   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4359   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4360   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4361   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4362
4363   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_1_content_too_big);
4364   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_1_content_enclosed);
4365   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4366   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4367   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4368   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4369
4370   cave->slime_predictable               = level->bd_slime_is_predictable;
4371   cave->slime_correct_random            = level->bd_slime_correct_random;
4372   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4373   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4374   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4375   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4376   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4377   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4378   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4379   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4380   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4381   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4382
4383   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4384   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4385   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4386
4387   cave->biter_delay_frame               = level->bd_biter_move_delay;
4388   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4389
4390   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4391
4392   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4393
4394   cave->replicators_active              = level->bd_replicators_active;
4395   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4396
4397   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4398   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4399
4400   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4401
4402   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4403
4404   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4405   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4406   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4407
4408   cave->infinite_rockets                = level->bd_infinite_rockets;
4409
4410   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4411   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4412
4413   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4414   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4415
4416   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4417   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4418   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4419
4420   cave->gravity                         = level->bd_gravity_direction;
4421   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4422   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4423   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4424
4425   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4426   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4427   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4428   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4429
4430   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_1_explodes_to);
4431   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4432   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_1_explodes_to);
4433   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4434   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4435   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4436
4437   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4438   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4439   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4440   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4441
4442   cave->colorb                          = level->bd_color_b;
4443   cave->color0                          = level->bd_color_0;
4444   cave->color1                          = level->bd_color_1;
4445   cave->color2                          = level->bd_color_2;
4446   cave->color3                          = level->bd_color_3;
4447   cave->color4                          = level->bd_color_4;
4448   cave->color5                          = level->bd_color_5;
4449
4450   // level name
4451   strncpy(cave->name, level->name, sizeof(GdString));
4452   cave->name[sizeof(GdString) - 1] = '\0';
4453
4454   // playfield elements
4455   for (x = 0; x < cave->w; x++)
4456     for (y = 0; y < cave->h; y++)
4457       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4458 }
4459
4460 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4461 {
4462   struct LevelInfo_BD *level_bd = level->native_bd_level;
4463   GdCave *cave = level_bd->cave;
4464   int bd_level_nr = level_bd->level_nr;
4465   int x, y;
4466
4467   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4468   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4469
4470   // level type
4471   level->bd_intermission                = cave->intermission;
4472
4473   // level settings
4474   level->time                           = cave->level_time[bd_level_nr];
4475   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4476
4477   // game timing
4478   level->bd_scheduling_type             = cave->scheduling;
4479   level->bd_pal_timing                  = cave->pal_timing;
4480   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4481   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4482   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4483   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4484
4485   // scores
4486   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4487   level->score[SC_EMERALD]              = cave->diamond_value;
4488   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4489
4490   // compatibility settings
4491   level->bd_line_shifting_borders       = cave->lineshift;
4492   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4493   level->bd_short_explosions            = cave->short_explosions;
4494
4495   // player properties
4496   level->bd_diagonal_movements          = cave->diagonal_movements;
4497   level->bd_topmost_player_active       = cave->active_is_first_found;
4498   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4499   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4500   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4501   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4502
4503   // element properties
4504   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4505   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4506   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4507   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4508   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4509   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4510   level->bd_magic_wall_time             = cave->level_magic_wall_time[bd_level_nr];
4511   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4512   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4513   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4514   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4515
4516   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4517   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4518   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4519   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4520   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4521   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4522   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4523
4524   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4525   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4526   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4527   level->bd_amoeba_1_threshold_too_big  = cave->level_amoeba_threshold[bd_level_nr];
4528   level->bd_amoeba_1_slow_growth_time   = cave->level_amoeba_time[bd_level_nr];
4529   level->bd_amoeba_1_slow_growth_rate   = cave->amoeba_growth_prob      / 10000;
4530   level->bd_amoeba_1_fast_growth_rate   = cave->amoeba_fast_growth_prob / 10000;
4531   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4532   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4533   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4534   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4535
4536   level->bd_amoeba_1_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4537   level->bd_amoeba_1_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4538   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4539   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4540   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4541   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4542
4543   level->bd_slime_is_predictable        = cave->slime_predictable;
4544   level->bd_slime_correct_random        = cave->slime_correct_random;
4545   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4546   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4547   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4548   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4549   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4550   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4551   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4552   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4553   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4554   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4555
4556   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4557   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4558   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4559
4560   level->bd_biter_move_delay            = cave->biter_delay_frame;
4561   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4562
4563   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4564
4565   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4566
4567   level->bd_replicators_active          = cave->replicators_active;
4568   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4569
4570   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4571   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4572
4573   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4574
4575   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4576
4577   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4578   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4579   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4580
4581   level->bd_infinite_rockets            = cave->infinite_rockets;
4582
4583   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4584   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4585
4586   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4587   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4588
4589   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4590   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4591   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4592
4593   level->bd_gravity_direction           = cave->gravity;
4594   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4595   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4596   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4597
4598   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4599   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4600   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4601   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4602
4603   level->bd_firefly_1_explodes_to       = CAVE_TO_LEVEL(cave->firefly_explode_to);
4604   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4605   level->bd_butterfly_1_explodes_to     = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4606   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4607   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4608   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4609
4610   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4611   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4612   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4613   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4614
4615   level->bd_color_b                     = cave->colorb;
4616   level->bd_color_0                     = cave->color0;
4617   level->bd_color_1                     = cave->color1;
4618   level->bd_color_2                     = cave->color2;
4619   level->bd_color_3                     = cave->color3;
4620   level->bd_color_4                     = cave->color4;
4621   level->bd_color_5                     = cave->color5;
4622
4623   // set default color type and colors for BD style level colors
4624   SetDefaultLevelColorType_BD();
4625   SetDefaultLevelColors_BD();
4626
4627   // level name
4628   char *cave_name_latin1 = getLatin1FromUTF8(cave->name);
4629   char *cave_name_final = (gd_caveset_has_levels() ?
4630                            getStringPrint("%s / %d", cave_name_latin1, bd_level_nr + 1) :
4631                            getStringCopy(cave_name_latin1));
4632
4633   strncpy(level->name, cave_name_final, MAX_LEVEL_NAME_LEN);
4634   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4635
4636   // playfield elements
4637   for (x = 0; x < level->fieldx; x++)
4638     for (y = 0; y < level->fieldy; y++)
4639       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4640
4641   checked_free(cave_name_latin1);
4642   checked_free(cave_name_final);
4643 }
4644
4645 static void setTapeInfoToDefaults(void);
4646
4647 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4648 {
4649   struct LevelInfo_BD *level_bd = level->native_bd_level;
4650   GdCave *cave = level_bd->cave;
4651   GdReplay *replay = level_bd->replay;
4652   int i;
4653
4654   if (replay == NULL)
4655     return;
4656
4657   // always start with reliable default values
4658   setTapeInfoToDefaults();
4659
4660   tape.level_nr = level_nr;             // (currently not used)
4661   tape.random_seed = replay->seed;
4662
4663   TapeSetDateFromIsoDateString(replay->date);
4664
4665   tape.counter = 0;
4666   tape.pos[tape.counter].delay = 0;
4667
4668   tape.bd_replay = TRUE;
4669
4670   // all time calculations only used to display approximate tape time
4671   int cave_speed = cave->speed;
4672   int milliseconds_game = 0;
4673   int milliseconds_elapsed = 20;
4674
4675   for (i = 0; i < replay->movements->len; i++)
4676   {
4677     int replay_action = replay->movements->data[i];
4678     int tape_action = map_action_BD_to_RND(replay_action);
4679     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4680     boolean success = 0;
4681
4682     while (1)
4683     {
4684       success = TapeAddAction(action);
4685
4686       milliseconds_game += milliseconds_elapsed;
4687
4688       if (milliseconds_game >= cave_speed)
4689       {
4690         milliseconds_game -= cave_speed;
4691
4692         break;
4693       }
4694     }
4695
4696     tape.counter++;
4697     tape.pos[tape.counter].delay = 0;
4698     tape.pos[tape.counter].action[0] = 0;
4699
4700     if (!success)
4701     {
4702       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4703
4704       break;
4705     }
4706   }
4707
4708   TapeHaltRecording();
4709
4710   if (!replay->success)
4711     Warn("BD replay is marked as not successful");
4712 }
4713
4714
4715 // ----------------------------------------------------------------------------
4716 // functions for loading EM level
4717 // ----------------------------------------------------------------------------
4718
4719 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4720 {
4721   static int ball_xy[8][2] =
4722   {
4723     { 0, 0 },
4724     { 1, 0 },
4725     { 2, 0 },
4726     { 0, 1 },
4727     { 2, 1 },
4728     { 0, 2 },
4729     { 1, 2 },
4730     { 2, 2 },
4731   };
4732   struct LevelInfo_EM *level_em = level->native_em_level;
4733   struct CAVE *cav = level_em->cav;
4734   int i, j, x, y;
4735
4736   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4737   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4738
4739   cav->time_seconds     = level->time;
4740   cav->gems_needed      = level->gems_needed;
4741
4742   cav->emerald_score    = level->score[SC_EMERALD];
4743   cav->diamond_score    = level->score[SC_DIAMOND];
4744   cav->alien_score      = level->score[SC_ROBOT];
4745   cav->tank_score       = level->score[SC_SPACESHIP];
4746   cav->bug_score        = level->score[SC_BUG];
4747   cav->eater_score      = level->score[SC_YAMYAM];
4748   cav->nut_score        = level->score[SC_NUT];
4749   cav->dynamite_score   = level->score[SC_DYNAMITE];
4750   cav->key_score        = level->score[SC_KEY];
4751   cav->exit_score       = level->score[SC_TIME_BONUS];
4752
4753   cav->num_eater_arrays = level->num_yamyam_contents;
4754
4755   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4756     for (y = 0; y < 3; y++)
4757       for (x = 0; x < 3; x++)
4758         cav->eater_array[i][y * 3 + x] =
4759           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4760
4761   cav->amoeba_time              = level->amoeba_speed;
4762   cav->wonderwall_time          = level->time_magic_wall;
4763   cav->wheel_time               = level->time_wheel;
4764
4765   cav->android_move_time        = level->android_move_time;
4766   cav->android_clone_time       = level->android_clone_time;
4767   cav->ball_random              = level->ball_random;
4768   cav->ball_active              = level->ball_active_initial;
4769   cav->ball_time                = level->ball_time;
4770   cav->num_ball_arrays          = level->num_ball_contents;
4771
4772   cav->lenses_score             = level->lenses_score;
4773   cav->magnify_score            = level->magnify_score;
4774   cav->slurp_score              = level->slurp_score;
4775
4776   cav->lenses_time              = level->lenses_time;
4777   cav->magnify_time             = level->magnify_time;
4778
4779   cav->wind_time = 9999;
4780   cav->wind_direction =
4781     map_direction_RND_to_EM(level->wind_direction_initial);
4782
4783   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4784     for (j = 0; j < 8; j++)
4785       cav->ball_array[i][j] =
4786         map_element_RND_to_EM_cave(level->ball_content[i].
4787                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4788
4789   map_android_clone_elements_RND_to_EM(level);
4790
4791   // first fill the complete playfield with the empty space element
4792   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4793     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4794       cav->cave[x][y] = Cblank;
4795
4796   // then copy the real level contents from level file into the playfield
4797   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4798   {
4799     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4800
4801     if (level->field[x][y] == EL_AMOEBA_DEAD)
4802       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4803
4804     cav->cave[x][y] = new_element;
4805   }
4806
4807   for (i = 0; i < MAX_PLAYERS; i++)
4808   {
4809     cav->player_x[i] = -1;
4810     cav->player_y[i] = -1;
4811   }
4812
4813   // initialize player positions and delete players from the playfield
4814   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4815   {
4816     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4817     {
4818       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4819
4820       cav->player_x[player_nr] = x;
4821       cav->player_y[player_nr] = y;
4822
4823       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4824     }
4825   }
4826 }
4827
4828 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4829 {
4830   static int ball_xy[8][2] =
4831   {
4832     { 0, 0 },
4833     { 1, 0 },
4834     { 2, 0 },
4835     { 0, 1 },
4836     { 2, 1 },
4837     { 0, 2 },
4838     { 1, 2 },
4839     { 2, 2 },
4840   };
4841   struct LevelInfo_EM *level_em = level->native_em_level;
4842   struct CAVE *cav = level_em->cav;
4843   int i, j, x, y;
4844
4845   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4846   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4847
4848   level->time        = cav->time_seconds;
4849   level->gems_needed = cav->gems_needed;
4850
4851   sprintf(level->name, "Level %d", level->file_info.nr);
4852
4853   level->score[SC_EMERALD]      = cav->emerald_score;
4854   level->score[SC_DIAMOND]      = cav->diamond_score;
4855   level->score[SC_ROBOT]        = cav->alien_score;
4856   level->score[SC_SPACESHIP]    = cav->tank_score;
4857   level->score[SC_BUG]          = cav->bug_score;
4858   level->score[SC_YAMYAM]       = cav->eater_score;
4859   level->score[SC_NUT]          = cav->nut_score;
4860   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4861   level->score[SC_KEY]          = cav->key_score;
4862   level->score[SC_TIME_BONUS]   = cav->exit_score;
4863
4864   level->num_yamyam_contents    = cav->num_eater_arrays;
4865
4866   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4867     for (y = 0; y < 3; y++)
4868       for (x = 0; x < 3; x++)
4869         level->yamyam_content[i].e[x][y] =
4870           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4871
4872   level->amoeba_speed           = cav->amoeba_time;
4873   level->time_magic_wall        = cav->wonderwall_time;
4874   level->time_wheel             = cav->wheel_time;
4875
4876   level->android_move_time      = cav->android_move_time;
4877   level->android_clone_time     = cav->android_clone_time;
4878   level->ball_random            = cav->ball_random;
4879   level->ball_active_initial    = cav->ball_active;
4880   level->ball_time              = cav->ball_time;
4881   level->num_ball_contents      = cav->num_ball_arrays;
4882
4883   level->lenses_score           = cav->lenses_score;
4884   level->magnify_score          = cav->magnify_score;
4885   level->slurp_score            = cav->slurp_score;
4886
4887   level->lenses_time            = cav->lenses_time;
4888   level->magnify_time           = cav->magnify_time;
4889
4890   level->wind_direction_initial =
4891     map_direction_EM_to_RND(cav->wind_direction);
4892
4893   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4894     for (j = 0; j < 8; j++)
4895       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4896         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4897
4898   map_android_clone_elements_EM_to_RND(level);
4899
4900   // convert the playfield (some elements need special treatment)
4901   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4902   {
4903     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4904
4905     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4906       new_element = EL_AMOEBA_DEAD;
4907
4908     level->field[x][y] = new_element;
4909   }
4910
4911   for (i = 0; i < MAX_PLAYERS; i++)
4912   {
4913     // in case of all players set to the same field, use the first player
4914     int nr = MAX_PLAYERS - i - 1;
4915     int jx = cav->player_x[nr];
4916     int jy = cav->player_y[nr];
4917
4918     if (jx != -1 && jy != -1)
4919       level->field[jx][jy] = EL_PLAYER_1 + nr;
4920   }
4921
4922   // time score is counted for each 10 seconds left in Emerald Mine levels
4923   level->time_score_base = 10;
4924 }
4925
4926
4927 // ----------------------------------------------------------------------------
4928 // functions for loading SP level
4929 // ----------------------------------------------------------------------------
4930
4931 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4932 {
4933   struct LevelInfo_SP *level_sp = level->native_sp_level;
4934   LevelInfoType *header = &level_sp->header;
4935   int i, x, y;
4936
4937   level_sp->width  = level->fieldx;
4938   level_sp->height = level->fieldy;
4939
4940   for (x = 0; x < level->fieldx; x++)
4941     for (y = 0; y < level->fieldy; y++)
4942       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4943
4944   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4945
4946   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4947     header->LevelTitle[i] = level->name[i];
4948   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4949
4950   header->InfotronsNeeded = level->gems_needed;
4951
4952   header->SpecialPortCount = 0;
4953
4954   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4955   {
4956     boolean gravity_port_found = FALSE;
4957     boolean gravity_port_valid = FALSE;
4958     int gravity_port_flag;
4959     int gravity_port_base_element;
4960     int element = level->field[x][y];
4961
4962     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4963         element <= EL_SP_GRAVITY_ON_PORT_UP)
4964     {
4965       gravity_port_found = TRUE;
4966       gravity_port_valid = TRUE;
4967       gravity_port_flag = 1;
4968       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4969     }
4970     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4971              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4972     {
4973       gravity_port_found = TRUE;
4974       gravity_port_valid = TRUE;
4975       gravity_port_flag = 0;
4976       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4977     }
4978     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4979              element <= EL_SP_GRAVITY_PORT_UP)
4980     {
4981       // change R'n'D style gravity inverting special port to normal port
4982       // (there are no gravity inverting ports in native Supaplex engine)
4983
4984       gravity_port_found = TRUE;
4985       gravity_port_valid = FALSE;
4986       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4987     }
4988
4989     if (gravity_port_found)
4990     {
4991       if (gravity_port_valid &&
4992           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4993       {
4994         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4995
4996         port->PortLocation = (y * level->fieldx + x) * 2;
4997         port->Gravity = gravity_port_flag;
4998
4999         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
5000
5001         header->SpecialPortCount++;
5002       }
5003       else
5004       {
5005         // change special gravity port to normal port
5006
5007         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
5008       }
5009
5010       level_sp->playfield[x][y] = element - EL_SP_START;
5011     }
5012   }
5013 }
5014
5015 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
5016 {
5017   struct LevelInfo_SP *level_sp = level->native_sp_level;
5018   LevelInfoType *header = &level_sp->header;
5019   boolean num_invalid_elements = 0;
5020   int i, j, x, y;
5021
5022   level->fieldx = level_sp->width;
5023   level->fieldy = level_sp->height;
5024
5025   for (x = 0; x < level->fieldx; x++)
5026   {
5027     for (y = 0; y < level->fieldy; y++)
5028     {
5029       int element_old = level_sp->playfield[x][y];
5030       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5031
5032       if (element_new == EL_UNKNOWN)
5033       {
5034         num_invalid_elements++;
5035
5036         Debug("level:native:SP", "invalid element %d at position %d, %d",
5037               element_old, x, y);
5038       }
5039
5040       level->field[x][y] = element_new;
5041     }
5042   }
5043
5044   if (num_invalid_elements > 0)
5045     Warn("found %d invalid elements%s", num_invalid_elements,
5046          (!options.debug ? " (use '--debug' for more details)" : ""));
5047
5048   for (i = 0; i < MAX_PLAYERS; i++)
5049     level->initial_player_gravity[i] =
5050       (header->InitialGravity == 1 ? TRUE : FALSE);
5051
5052   // skip leading spaces
5053   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5054     if (header->LevelTitle[i] != ' ')
5055       break;
5056
5057   // copy level title
5058   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5059     level->name[j] = header->LevelTitle[i];
5060   level->name[j] = '\0';
5061
5062   // cut trailing spaces
5063   for (; j > 0; j--)
5064     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5065       level->name[j - 1] = '\0';
5066
5067   level->gems_needed = header->InfotronsNeeded;
5068
5069   for (i = 0; i < header->SpecialPortCount; i++)
5070   {
5071     SpecialPortType *port = &header->SpecialPort[i];
5072     int port_location = port->PortLocation;
5073     int gravity = port->Gravity;
5074     int port_x, port_y, port_element;
5075
5076     port_x = (port_location / 2) % level->fieldx;
5077     port_y = (port_location / 2) / level->fieldx;
5078
5079     if (port_x < 0 || port_x >= level->fieldx ||
5080         port_y < 0 || port_y >= level->fieldy)
5081     {
5082       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5083
5084       continue;
5085     }
5086
5087     port_element = level->field[port_x][port_y];
5088
5089     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5090         port_element > EL_SP_GRAVITY_PORT_UP)
5091     {
5092       Warn("no special port at position (%d, %d)", port_x, port_y);
5093
5094       continue;
5095     }
5096
5097     // change previous (wrong) gravity inverting special port to either
5098     // gravity enabling special port or gravity disabling special port
5099     level->field[port_x][port_y] +=
5100       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5101        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5102   }
5103
5104   // change special gravity ports without database entries to normal ports
5105   for (x = 0; x < level->fieldx; x++)
5106     for (y = 0; y < level->fieldy; y++)
5107       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5108           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5109         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5110
5111   level->time = 0;                      // no time limit
5112   level->amoeba_speed = 0;
5113   level->time_magic_wall = 0;
5114   level->time_wheel = 0;
5115   level->amoeba_content = EL_EMPTY;
5116
5117   // original Supaplex does not use score values -- rate by playing time
5118   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5119     level->score[i] = 0;
5120
5121   level->rate_time_over_score = TRUE;
5122
5123   // there are no yamyams in supaplex levels
5124   for (i = 0; i < level->num_yamyam_contents; i++)
5125     for (x = 0; x < 3; x++)
5126       for (y = 0; y < 3; y++)
5127         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5128 }
5129
5130 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5131 {
5132   struct LevelInfo_SP *level_sp = level->native_sp_level;
5133   struct DemoInfo_SP *demo = &level_sp->demo;
5134   int i, j;
5135
5136   // always start with reliable default values
5137   demo->is_available = FALSE;
5138   demo->length = 0;
5139
5140   if (TAPE_IS_EMPTY(tape))
5141     return;
5142
5143   demo->level_nr = tape.level_nr;       // (currently not used)
5144
5145   level_sp->header.DemoRandomSeed = tape.random_seed;
5146
5147   demo->length = 0;
5148
5149   for (i = 0; i < tape.length; i++)
5150   {
5151     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5152     int demo_repeat = tape.pos[i].delay;
5153     int demo_entries = (demo_repeat + 15) / 16;
5154
5155     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5156     {
5157       Warn("tape truncated: size exceeds maximum SP demo size %d",
5158            SP_MAX_TAPE_LEN);
5159
5160       break;
5161     }
5162
5163     for (j = 0; j < demo_repeat / 16; j++)
5164       demo->data[demo->length++] = 0xf0 | demo_action;
5165
5166     if (demo_repeat % 16)
5167       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5168   }
5169
5170   demo->is_available = TRUE;
5171 }
5172
5173 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5174 {
5175   struct LevelInfo_SP *level_sp = level->native_sp_level;
5176   struct DemoInfo_SP *demo = &level_sp->demo;
5177   char *filename = level->file_info.filename;
5178   int i;
5179
5180   // always start with reliable default values
5181   setTapeInfoToDefaults();
5182
5183   if (!demo->is_available)
5184     return;
5185
5186   tape.level_nr = demo->level_nr;       // (currently not used)
5187   tape.random_seed = level_sp->header.DemoRandomSeed;
5188
5189   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5190
5191   tape.counter = 0;
5192   tape.pos[tape.counter].delay = 0;
5193
5194   for (i = 0; i < demo->length; i++)
5195   {
5196     int demo_action = demo->data[i] & 0x0f;
5197     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5198     int tape_action = map_key_SP_to_RND(demo_action);
5199     int tape_repeat = demo_repeat + 1;
5200     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5201     boolean success = 0;
5202     int j;
5203
5204     for (j = 0; j < tape_repeat; j++)
5205       success = TapeAddAction(action);
5206
5207     if (!success)
5208     {
5209       Warn("SP demo truncated: size exceeds maximum tape size %d",
5210            MAX_TAPE_LEN);
5211
5212       break;
5213     }
5214   }
5215
5216   TapeHaltRecording();
5217 }
5218
5219
5220 // ----------------------------------------------------------------------------
5221 // functions for loading MM level
5222 // ----------------------------------------------------------------------------
5223
5224 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5225 {
5226   struct LevelInfo_MM *level_mm = level->native_mm_level;
5227   int i, x, y;
5228
5229   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5230   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5231
5232   level_mm->time = level->time;
5233   level_mm->kettles_needed = level->gems_needed;
5234   level_mm->auto_count_kettles = level->auto_count_gems;
5235
5236   level_mm->mm_laser_red   = level->mm_laser_red;
5237   level_mm->mm_laser_green = level->mm_laser_green;
5238   level_mm->mm_laser_blue  = level->mm_laser_blue;
5239
5240   level_mm->df_laser_red   = level->df_laser_red;
5241   level_mm->df_laser_green = level->df_laser_green;
5242   level_mm->df_laser_blue  = level->df_laser_blue;
5243
5244   strcpy(level_mm->name, level->name);
5245   strcpy(level_mm->author, level->author);
5246
5247   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5248   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5249   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5250   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5251   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5252
5253   level_mm->amoeba_speed = level->amoeba_speed;
5254   level_mm->time_fuse    = level->mm_time_fuse;
5255   level_mm->time_bomb    = level->mm_time_bomb;
5256   level_mm->time_ball    = level->mm_time_ball;
5257   level_mm->time_block   = level->mm_time_block;
5258
5259   level_mm->num_ball_contents = level->num_mm_ball_contents;
5260   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5261   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5262   level_mm->explode_ball = level->explode_mm_ball;
5263
5264   for (i = 0; i < level->num_mm_ball_contents; i++)
5265     level_mm->ball_content[i] =
5266       map_element_RND_to_MM(level->mm_ball_content[i]);
5267
5268   for (x = 0; x < level->fieldx; x++)
5269     for (y = 0; y < level->fieldy; y++)
5270       Ur[x][y] =
5271         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5272 }
5273
5274 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5275 {
5276   struct LevelInfo_MM *level_mm = level->native_mm_level;
5277   int i, x, y;
5278
5279   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5280   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5281
5282   level->time = level_mm->time;
5283   level->gems_needed = level_mm->kettles_needed;
5284   level->auto_count_gems = level_mm->auto_count_kettles;
5285
5286   level->mm_laser_red   = level_mm->mm_laser_red;
5287   level->mm_laser_green = level_mm->mm_laser_green;
5288   level->mm_laser_blue  = level_mm->mm_laser_blue;
5289
5290   level->df_laser_red   = level_mm->df_laser_red;
5291   level->df_laser_green = level_mm->df_laser_green;
5292   level->df_laser_blue  = level_mm->df_laser_blue;
5293
5294   strcpy(level->name, level_mm->name);
5295
5296   // only overwrite author from 'levelinfo.conf' if author defined in level
5297   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5298     strcpy(level->author, level_mm->author);
5299
5300   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5301   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5302   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5303   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5304   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5305
5306   level->amoeba_speed  = level_mm->amoeba_speed;
5307   level->mm_time_fuse  = level_mm->time_fuse;
5308   level->mm_time_bomb  = level_mm->time_bomb;
5309   level->mm_time_ball  = level_mm->time_ball;
5310   level->mm_time_block = level_mm->time_block;
5311
5312   level->num_mm_ball_contents = level_mm->num_ball_contents;
5313   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5314   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5315   level->explode_mm_ball = level_mm->explode_ball;
5316
5317   for (i = 0; i < level->num_mm_ball_contents; i++)
5318     level->mm_ball_content[i] =
5319       map_element_MM_to_RND(level_mm->ball_content[i]);
5320
5321   for (x = 0; x < level->fieldx; x++)
5322     for (y = 0; y < level->fieldy; y++)
5323       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5324 }
5325
5326
5327 // ----------------------------------------------------------------------------
5328 // functions for loading DC level
5329 // ----------------------------------------------------------------------------
5330
5331 #define DC_LEVEL_HEADER_SIZE            344
5332
5333 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5334                                         boolean init)
5335 {
5336   static int last_data_encoded;
5337   static int offset1;
5338   static int offset2;
5339   int diff;
5340   int diff_hi, diff_lo;
5341   int data_hi, data_lo;
5342   unsigned short data_decoded;
5343
5344   if (init)
5345   {
5346     last_data_encoded = 0;
5347     offset1 = -1;
5348     offset2 = 0;
5349
5350     return 0;
5351   }
5352
5353   diff = data_encoded - last_data_encoded;
5354   diff_hi = diff & ~0xff;
5355   diff_lo = diff &  0xff;
5356
5357   offset2 += diff_lo;
5358
5359   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5360   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5361   data_hi = data_hi & 0xff00;
5362
5363   data_decoded = data_hi | data_lo;
5364
5365   last_data_encoded = data_encoded;
5366
5367   offset1 = (offset1 + 1) % 31;
5368   offset2 = offset2 & 0xff;
5369
5370   return data_decoded;
5371 }
5372
5373 static int getMappedElement_DC(int element)
5374 {
5375   switch (element)
5376   {
5377     case 0x0000:
5378       element = EL_ROCK;
5379       break;
5380
5381       // 0x0117 - 0x036e: (?)
5382       // EL_DIAMOND
5383
5384       // 0x042d - 0x0684: (?)
5385       // EL_EMERALD
5386
5387     case 0x06f1:
5388       element = EL_NUT;
5389       break;
5390
5391     case 0x074c:
5392       element = EL_BOMB;
5393       break;
5394
5395     case 0x07a4:
5396       element = EL_PEARL;
5397       break;
5398
5399     case 0x0823:
5400       element = EL_CRYSTAL;
5401       break;
5402
5403     case 0x0e77:        // quicksand (boulder)
5404       element = EL_QUICKSAND_FAST_FULL;
5405       break;
5406
5407     case 0x0e99:        // slow quicksand (boulder)
5408       element = EL_QUICKSAND_FULL;
5409       break;
5410
5411     case 0x0ed2:
5412       element = EL_EM_EXIT_OPEN;
5413       break;
5414
5415     case 0x0ee3:
5416       element = EL_EM_EXIT_CLOSED;
5417       break;
5418
5419     case 0x0eeb:
5420       element = EL_EM_STEEL_EXIT_OPEN;
5421       break;
5422
5423     case 0x0efc:
5424       element = EL_EM_STEEL_EXIT_CLOSED;
5425       break;
5426
5427     case 0x0f4f:        // dynamite (lit 1)
5428       element = EL_EM_DYNAMITE_ACTIVE;
5429       break;
5430
5431     case 0x0f57:        // dynamite (lit 2)
5432       element = EL_EM_DYNAMITE_ACTIVE;
5433       break;
5434
5435     case 0x0f5f:        // dynamite (lit 3)
5436       element = EL_EM_DYNAMITE_ACTIVE;
5437       break;
5438
5439     case 0x0f67:        // dynamite (lit 4)
5440       element = EL_EM_DYNAMITE_ACTIVE;
5441       break;
5442
5443     case 0x0f81:
5444     case 0x0f82:
5445     case 0x0f83:
5446     case 0x0f84:
5447       element = EL_AMOEBA_WET;
5448       break;
5449
5450     case 0x0f85:
5451       element = EL_AMOEBA_DROP;
5452       break;
5453
5454     case 0x0fb9:
5455       element = EL_DC_MAGIC_WALL;
5456       break;
5457
5458     case 0x0fd0:
5459       element = EL_SPACESHIP_UP;
5460       break;
5461
5462     case 0x0fd9:
5463       element = EL_SPACESHIP_DOWN;
5464       break;
5465
5466     case 0x0ff1:
5467       element = EL_SPACESHIP_LEFT;
5468       break;
5469
5470     case 0x0ff9:
5471       element = EL_SPACESHIP_RIGHT;
5472       break;
5473
5474     case 0x1057:
5475       element = EL_BUG_UP;
5476       break;
5477
5478     case 0x1060:
5479       element = EL_BUG_DOWN;
5480       break;
5481
5482     case 0x1078:
5483       element = EL_BUG_LEFT;
5484       break;
5485
5486     case 0x1080:
5487       element = EL_BUG_RIGHT;
5488       break;
5489
5490     case 0x10de:
5491       element = EL_MOLE_UP;
5492       break;
5493
5494     case 0x10e7:
5495       element = EL_MOLE_DOWN;
5496       break;
5497
5498     case 0x10ff:
5499       element = EL_MOLE_LEFT;
5500       break;
5501
5502     case 0x1107:
5503       element = EL_MOLE_RIGHT;
5504       break;
5505
5506     case 0x11c0:
5507       element = EL_ROBOT;
5508       break;
5509
5510     case 0x13f5:
5511       element = EL_YAMYAM_UP;
5512       break;
5513
5514     case 0x1425:
5515       element = EL_SWITCHGATE_OPEN;
5516       break;
5517
5518     case 0x1426:
5519       element = EL_SWITCHGATE_CLOSED;
5520       break;
5521
5522     case 0x1437:
5523       element = EL_DC_SWITCHGATE_SWITCH_UP;
5524       break;
5525
5526     case 0x143a:
5527       element = EL_TIMEGATE_CLOSED;
5528       break;
5529
5530     case 0x144c:        // conveyor belt switch (green)
5531       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5532       break;
5533
5534     case 0x144f:        // conveyor belt switch (red)
5535       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5536       break;
5537
5538     case 0x1452:        // conveyor belt switch (blue)
5539       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5540       break;
5541
5542     case 0x145b:
5543       element = EL_CONVEYOR_BELT_3_MIDDLE;
5544       break;
5545
5546     case 0x1463:
5547       element = EL_CONVEYOR_BELT_3_LEFT;
5548       break;
5549
5550     case 0x146b:
5551       element = EL_CONVEYOR_BELT_3_RIGHT;
5552       break;
5553
5554     case 0x1473:
5555       element = EL_CONVEYOR_BELT_1_MIDDLE;
5556       break;
5557
5558     case 0x147b:
5559       element = EL_CONVEYOR_BELT_1_LEFT;
5560       break;
5561
5562     case 0x1483:
5563       element = EL_CONVEYOR_BELT_1_RIGHT;
5564       break;
5565
5566     case 0x148b:
5567       element = EL_CONVEYOR_BELT_4_MIDDLE;
5568       break;
5569
5570     case 0x1493:
5571       element = EL_CONVEYOR_BELT_4_LEFT;
5572       break;
5573
5574     case 0x149b:
5575       element = EL_CONVEYOR_BELT_4_RIGHT;
5576       break;
5577
5578     case 0x14ac:
5579       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5580       break;
5581
5582     case 0x14bd:
5583       element = EL_EXPANDABLE_WALL_VERTICAL;
5584       break;
5585
5586     case 0x14c6:
5587       element = EL_EXPANDABLE_WALL_ANY;
5588       break;
5589
5590     case 0x14ce:        // growing steel wall (left/right)
5591       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5592       break;
5593
5594     case 0x14df:        // growing steel wall (up/down)
5595       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5596       break;
5597
5598     case 0x14e8:        // growing steel wall (up/down/left/right)
5599       element = EL_EXPANDABLE_STEELWALL_ANY;
5600       break;
5601
5602     case 0x14e9:
5603       element = EL_SHIELD_DEADLY;
5604       break;
5605
5606     case 0x1501:
5607       element = EL_EXTRA_TIME;
5608       break;
5609
5610     case 0x154f:
5611       element = EL_ACID;
5612       break;
5613
5614     case 0x1577:
5615       element = EL_EMPTY_SPACE;
5616       break;
5617
5618     case 0x1578:        // quicksand (empty)
5619       element = EL_QUICKSAND_FAST_EMPTY;
5620       break;
5621
5622     case 0x1579:        // slow quicksand (empty)
5623       element = EL_QUICKSAND_EMPTY;
5624       break;
5625
5626       // 0x157c - 0x158b:
5627       // EL_SAND
5628
5629       // 0x1590 - 0x159f:
5630       // EL_DC_LANDMINE
5631
5632     case 0x15a0:
5633       element = EL_EM_DYNAMITE;
5634       break;
5635
5636     case 0x15a1:        // key (red)
5637       element = EL_EM_KEY_1;
5638       break;
5639
5640     case 0x15a2:        // key (yellow)
5641       element = EL_EM_KEY_2;
5642       break;
5643
5644     case 0x15a3:        // key (blue)
5645       element = EL_EM_KEY_4;
5646       break;
5647
5648     case 0x15a4:        // key (green)
5649       element = EL_EM_KEY_3;
5650       break;
5651
5652     case 0x15a5:        // key (white)
5653       element = EL_DC_KEY_WHITE;
5654       break;
5655
5656     case 0x15a6:
5657       element = EL_WALL_SLIPPERY;
5658       break;
5659
5660     case 0x15a7:
5661       element = EL_WALL;
5662       break;
5663
5664     case 0x15a8:        // wall (not round)
5665       element = EL_WALL;
5666       break;
5667
5668     case 0x15a9:        // (blue)
5669       element = EL_CHAR_A;
5670       break;
5671
5672     case 0x15aa:        // (blue)
5673       element = EL_CHAR_B;
5674       break;
5675
5676     case 0x15ab:        // (blue)
5677       element = EL_CHAR_C;
5678       break;
5679
5680     case 0x15ac:        // (blue)
5681       element = EL_CHAR_D;
5682       break;
5683
5684     case 0x15ad:        // (blue)
5685       element = EL_CHAR_E;
5686       break;
5687
5688     case 0x15ae:        // (blue)
5689       element = EL_CHAR_F;
5690       break;
5691
5692     case 0x15af:        // (blue)
5693       element = EL_CHAR_G;
5694       break;
5695
5696     case 0x15b0:        // (blue)
5697       element = EL_CHAR_H;
5698       break;
5699
5700     case 0x15b1:        // (blue)
5701       element = EL_CHAR_I;
5702       break;
5703
5704     case 0x15b2:        // (blue)
5705       element = EL_CHAR_J;
5706       break;
5707
5708     case 0x15b3:        // (blue)
5709       element = EL_CHAR_K;
5710       break;
5711
5712     case 0x15b4:        // (blue)
5713       element = EL_CHAR_L;
5714       break;
5715
5716     case 0x15b5:        // (blue)
5717       element = EL_CHAR_M;
5718       break;
5719
5720     case 0x15b6:        // (blue)
5721       element = EL_CHAR_N;
5722       break;
5723
5724     case 0x15b7:        // (blue)
5725       element = EL_CHAR_O;
5726       break;
5727
5728     case 0x15b8:        // (blue)
5729       element = EL_CHAR_P;
5730       break;
5731
5732     case 0x15b9:        // (blue)
5733       element = EL_CHAR_Q;
5734       break;
5735
5736     case 0x15ba:        // (blue)
5737       element = EL_CHAR_R;
5738       break;
5739
5740     case 0x15bb:        // (blue)
5741       element = EL_CHAR_S;
5742       break;
5743
5744     case 0x15bc:        // (blue)
5745       element = EL_CHAR_T;
5746       break;
5747
5748     case 0x15bd:        // (blue)
5749       element = EL_CHAR_U;
5750       break;
5751
5752     case 0x15be:        // (blue)
5753       element = EL_CHAR_V;
5754       break;
5755
5756     case 0x15bf:        // (blue)
5757       element = EL_CHAR_W;
5758       break;
5759
5760     case 0x15c0:        // (blue)
5761       element = EL_CHAR_X;
5762       break;
5763
5764     case 0x15c1:        // (blue)
5765       element = EL_CHAR_Y;
5766       break;
5767
5768     case 0x15c2:        // (blue)
5769       element = EL_CHAR_Z;
5770       break;
5771
5772     case 0x15c3:        // (blue)
5773       element = EL_CHAR_AUMLAUT;
5774       break;
5775
5776     case 0x15c4:        // (blue)
5777       element = EL_CHAR_OUMLAUT;
5778       break;
5779
5780     case 0x15c5:        // (blue)
5781       element = EL_CHAR_UUMLAUT;
5782       break;
5783
5784     case 0x15c6:        // (blue)
5785       element = EL_CHAR_0;
5786       break;
5787
5788     case 0x15c7:        // (blue)
5789       element = EL_CHAR_1;
5790       break;
5791
5792     case 0x15c8:        // (blue)
5793       element = EL_CHAR_2;
5794       break;
5795
5796     case 0x15c9:        // (blue)
5797       element = EL_CHAR_3;
5798       break;
5799
5800     case 0x15ca:        // (blue)
5801       element = EL_CHAR_4;
5802       break;
5803
5804     case 0x15cb:        // (blue)
5805       element = EL_CHAR_5;
5806       break;
5807
5808     case 0x15cc:        // (blue)
5809       element = EL_CHAR_6;
5810       break;
5811
5812     case 0x15cd:        // (blue)
5813       element = EL_CHAR_7;
5814       break;
5815
5816     case 0x15ce:        // (blue)
5817       element = EL_CHAR_8;
5818       break;
5819
5820     case 0x15cf:        // (blue)
5821       element = EL_CHAR_9;
5822       break;
5823
5824     case 0x15d0:        // (blue)
5825       element = EL_CHAR_PERIOD;
5826       break;
5827
5828     case 0x15d1:        // (blue)
5829       element = EL_CHAR_EXCLAM;
5830       break;
5831
5832     case 0x15d2:        // (blue)
5833       element = EL_CHAR_COLON;
5834       break;
5835
5836     case 0x15d3:        // (blue)
5837       element = EL_CHAR_LESS;
5838       break;
5839
5840     case 0x15d4:        // (blue)
5841       element = EL_CHAR_GREATER;
5842       break;
5843
5844     case 0x15d5:        // (blue)
5845       element = EL_CHAR_QUESTION;
5846       break;
5847
5848     case 0x15d6:        // (blue)
5849       element = EL_CHAR_COPYRIGHT;
5850       break;
5851
5852     case 0x15d7:        // (blue)
5853       element = EL_CHAR_UP;
5854       break;
5855
5856     case 0x15d8:        // (blue)
5857       element = EL_CHAR_DOWN;
5858       break;
5859
5860     case 0x15d9:        // (blue)
5861       element = EL_CHAR_BUTTON;
5862       break;
5863
5864     case 0x15da:        // (blue)
5865       element = EL_CHAR_PLUS;
5866       break;
5867
5868     case 0x15db:        // (blue)
5869       element = EL_CHAR_MINUS;
5870       break;
5871
5872     case 0x15dc:        // (blue)
5873       element = EL_CHAR_APOSTROPHE;
5874       break;
5875
5876     case 0x15dd:        // (blue)
5877       element = EL_CHAR_PARENLEFT;
5878       break;
5879
5880     case 0x15de:        // (blue)
5881       element = EL_CHAR_PARENRIGHT;
5882       break;
5883
5884     case 0x15df:        // (green)
5885       element = EL_CHAR_A;
5886       break;
5887
5888     case 0x15e0:        // (green)
5889       element = EL_CHAR_B;
5890       break;
5891
5892     case 0x15e1:        // (green)
5893       element = EL_CHAR_C;
5894       break;
5895
5896     case 0x15e2:        // (green)
5897       element = EL_CHAR_D;
5898       break;
5899
5900     case 0x15e3:        // (green)
5901       element = EL_CHAR_E;
5902       break;
5903
5904     case 0x15e4:        // (green)
5905       element = EL_CHAR_F;
5906       break;
5907
5908     case 0x15e5:        // (green)
5909       element = EL_CHAR_G;
5910       break;
5911
5912     case 0x15e6:        // (green)
5913       element = EL_CHAR_H;
5914       break;
5915
5916     case 0x15e7:        // (green)
5917       element = EL_CHAR_I;
5918       break;
5919
5920     case 0x15e8:        // (green)
5921       element = EL_CHAR_J;
5922       break;
5923
5924     case 0x15e9:        // (green)
5925       element = EL_CHAR_K;
5926       break;
5927
5928     case 0x15ea:        // (green)
5929       element = EL_CHAR_L;
5930       break;
5931
5932     case 0x15eb:        // (green)
5933       element = EL_CHAR_M;
5934       break;
5935
5936     case 0x15ec:        // (green)
5937       element = EL_CHAR_N;
5938       break;
5939
5940     case 0x15ed:        // (green)
5941       element = EL_CHAR_O;
5942       break;
5943
5944     case 0x15ee:        // (green)
5945       element = EL_CHAR_P;
5946       break;
5947
5948     case 0x15ef:        // (green)
5949       element = EL_CHAR_Q;
5950       break;
5951
5952     case 0x15f0:        // (green)
5953       element = EL_CHAR_R;
5954       break;
5955
5956     case 0x15f1:        // (green)
5957       element = EL_CHAR_S;
5958       break;
5959
5960     case 0x15f2:        // (green)
5961       element = EL_CHAR_T;
5962       break;
5963
5964     case 0x15f3:        // (green)
5965       element = EL_CHAR_U;
5966       break;
5967
5968     case 0x15f4:        // (green)
5969       element = EL_CHAR_V;
5970       break;
5971
5972     case 0x15f5:        // (green)
5973       element = EL_CHAR_W;
5974       break;
5975
5976     case 0x15f6:        // (green)
5977       element = EL_CHAR_X;
5978       break;
5979
5980     case 0x15f7:        // (green)
5981       element = EL_CHAR_Y;
5982       break;
5983
5984     case 0x15f8:        // (green)
5985       element = EL_CHAR_Z;
5986       break;
5987
5988     case 0x15f9:        // (green)
5989       element = EL_CHAR_AUMLAUT;
5990       break;
5991
5992     case 0x15fa:        // (green)
5993       element = EL_CHAR_OUMLAUT;
5994       break;
5995
5996     case 0x15fb:        // (green)
5997       element = EL_CHAR_UUMLAUT;
5998       break;
5999
6000     case 0x15fc:        // (green)
6001       element = EL_CHAR_0;
6002       break;
6003
6004     case 0x15fd:        // (green)
6005       element = EL_CHAR_1;
6006       break;
6007
6008     case 0x15fe:        // (green)
6009       element = EL_CHAR_2;
6010       break;
6011
6012     case 0x15ff:        // (green)
6013       element = EL_CHAR_3;
6014       break;
6015
6016     case 0x1600:        // (green)
6017       element = EL_CHAR_4;
6018       break;
6019
6020     case 0x1601:        // (green)
6021       element = EL_CHAR_5;
6022       break;
6023
6024     case 0x1602:        // (green)
6025       element = EL_CHAR_6;
6026       break;
6027
6028     case 0x1603:        // (green)
6029       element = EL_CHAR_7;
6030       break;
6031
6032     case 0x1604:        // (green)
6033       element = EL_CHAR_8;
6034       break;
6035
6036     case 0x1605:        // (green)
6037       element = EL_CHAR_9;
6038       break;
6039
6040     case 0x1606:        // (green)
6041       element = EL_CHAR_PERIOD;
6042       break;
6043
6044     case 0x1607:        // (green)
6045       element = EL_CHAR_EXCLAM;
6046       break;
6047
6048     case 0x1608:        // (green)
6049       element = EL_CHAR_COLON;
6050       break;
6051
6052     case 0x1609:        // (green)
6053       element = EL_CHAR_LESS;
6054       break;
6055
6056     case 0x160a:        // (green)
6057       element = EL_CHAR_GREATER;
6058       break;
6059
6060     case 0x160b:        // (green)
6061       element = EL_CHAR_QUESTION;
6062       break;
6063
6064     case 0x160c:        // (green)
6065       element = EL_CHAR_COPYRIGHT;
6066       break;
6067
6068     case 0x160d:        // (green)
6069       element = EL_CHAR_UP;
6070       break;
6071
6072     case 0x160e:        // (green)
6073       element = EL_CHAR_DOWN;
6074       break;
6075
6076     case 0x160f:        // (green)
6077       element = EL_CHAR_BUTTON;
6078       break;
6079
6080     case 0x1610:        // (green)
6081       element = EL_CHAR_PLUS;
6082       break;
6083
6084     case 0x1611:        // (green)
6085       element = EL_CHAR_MINUS;
6086       break;
6087
6088     case 0x1612:        // (green)
6089       element = EL_CHAR_APOSTROPHE;
6090       break;
6091
6092     case 0x1613:        // (green)
6093       element = EL_CHAR_PARENLEFT;
6094       break;
6095
6096     case 0x1614:        // (green)
6097       element = EL_CHAR_PARENRIGHT;
6098       break;
6099
6100     case 0x1615:        // (blue steel)
6101       element = EL_STEEL_CHAR_A;
6102       break;
6103
6104     case 0x1616:        // (blue steel)
6105       element = EL_STEEL_CHAR_B;
6106       break;
6107
6108     case 0x1617:        // (blue steel)
6109       element = EL_STEEL_CHAR_C;
6110       break;
6111
6112     case 0x1618:        // (blue steel)
6113       element = EL_STEEL_CHAR_D;
6114       break;
6115
6116     case 0x1619:        // (blue steel)
6117       element = EL_STEEL_CHAR_E;
6118       break;
6119
6120     case 0x161a:        // (blue steel)
6121       element = EL_STEEL_CHAR_F;
6122       break;
6123
6124     case 0x161b:        // (blue steel)
6125       element = EL_STEEL_CHAR_G;
6126       break;
6127
6128     case 0x161c:        // (blue steel)
6129       element = EL_STEEL_CHAR_H;
6130       break;
6131
6132     case 0x161d:        // (blue steel)
6133       element = EL_STEEL_CHAR_I;
6134       break;
6135
6136     case 0x161e:        // (blue steel)
6137       element = EL_STEEL_CHAR_J;
6138       break;
6139
6140     case 0x161f:        // (blue steel)
6141       element = EL_STEEL_CHAR_K;
6142       break;
6143
6144     case 0x1620:        // (blue steel)
6145       element = EL_STEEL_CHAR_L;
6146       break;
6147
6148     case 0x1621:        // (blue steel)
6149       element = EL_STEEL_CHAR_M;
6150       break;
6151
6152     case 0x1622:        // (blue steel)
6153       element = EL_STEEL_CHAR_N;
6154       break;
6155
6156     case 0x1623:        // (blue steel)
6157       element = EL_STEEL_CHAR_O;
6158       break;
6159
6160     case 0x1624:        // (blue steel)
6161       element = EL_STEEL_CHAR_P;
6162       break;
6163
6164     case 0x1625:        // (blue steel)
6165       element = EL_STEEL_CHAR_Q;
6166       break;
6167
6168     case 0x1626:        // (blue steel)
6169       element = EL_STEEL_CHAR_R;
6170       break;
6171
6172     case 0x1627:        // (blue steel)
6173       element = EL_STEEL_CHAR_S;
6174       break;
6175
6176     case 0x1628:        // (blue steel)
6177       element = EL_STEEL_CHAR_T;
6178       break;
6179
6180     case 0x1629:        // (blue steel)
6181       element = EL_STEEL_CHAR_U;
6182       break;
6183
6184     case 0x162a:        // (blue steel)
6185       element = EL_STEEL_CHAR_V;
6186       break;
6187
6188     case 0x162b:        // (blue steel)
6189       element = EL_STEEL_CHAR_W;
6190       break;
6191
6192     case 0x162c:        // (blue steel)
6193       element = EL_STEEL_CHAR_X;
6194       break;
6195
6196     case 0x162d:        // (blue steel)
6197       element = EL_STEEL_CHAR_Y;
6198       break;
6199
6200     case 0x162e:        // (blue steel)
6201       element = EL_STEEL_CHAR_Z;
6202       break;
6203
6204     case 0x162f:        // (blue steel)
6205       element = EL_STEEL_CHAR_AUMLAUT;
6206       break;
6207
6208     case 0x1630:        // (blue steel)
6209       element = EL_STEEL_CHAR_OUMLAUT;
6210       break;
6211
6212     case 0x1631:        // (blue steel)
6213       element = EL_STEEL_CHAR_UUMLAUT;
6214       break;
6215
6216     case 0x1632:        // (blue steel)
6217       element = EL_STEEL_CHAR_0;
6218       break;
6219
6220     case 0x1633:        // (blue steel)
6221       element = EL_STEEL_CHAR_1;
6222       break;
6223
6224     case 0x1634:        // (blue steel)
6225       element = EL_STEEL_CHAR_2;
6226       break;
6227
6228     case 0x1635:        // (blue steel)
6229       element = EL_STEEL_CHAR_3;
6230       break;
6231
6232     case 0x1636:        // (blue steel)
6233       element = EL_STEEL_CHAR_4;
6234       break;
6235
6236     case 0x1637:        // (blue steel)
6237       element = EL_STEEL_CHAR_5;
6238       break;
6239
6240     case 0x1638:        // (blue steel)
6241       element = EL_STEEL_CHAR_6;
6242       break;
6243
6244     case 0x1639:        // (blue steel)
6245       element = EL_STEEL_CHAR_7;
6246       break;
6247
6248     case 0x163a:        // (blue steel)
6249       element = EL_STEEL_CHAR_8;
6250       break;
6251
6252     case 0x163b:        // (blue steel)
6253       element = EL_STEEL_CHAR_9;
6254       break;
6255
6256     case 0x163c:        // (blue steel)
6257       element = EL_STEEL_CHAR_PERIOD;
6258       break;
6259
6260     case 0x163d:        // (blue steel)
6261       element = EL_STEEL_CHAR_EXCLAM;
6262       break;
6263
6264     case 0x163e:        // (blue steel)
6265       element = EL_STEEL_CHAR_COLON;
6266       break;
6267
6268     case 0x163f:        // (blue steel)
6269       element = EL_STEEL_CHAR_LESS;
6270       break;
6271
6272     case 0x1640:        // (blue steel)
6273       element = EL_STEEL_CHAR_GREATER;
6274       break;
6275
6276     case 0x1641:        // (blue steel)
6277       element = EL_STEEL_CHAR_QUESTION;
6278       break;
6279
6280     case 0x1642:        // (blue steel)
6281       element = EL_STEEL_CHAR_COPYRIGHT;
6282       break;
6283
6284     case 0x1643:        // (blue steel)
6285       element = EL_STEEL_CHAR_UP;
6286       break;
6287
6288     case 0x1644:        // (blue steel)
6289       element = EL_STEEL_CHAR_DOWN;
6290       break;
6291
6292     case 0x1645:        // (blue steel)
6293       element = EL_STEEL_CHAR_BUTTON;
6294       break;
6295
6296     case 0x1646:        // (blue steel)
6297       element = EL_STEEL_CHAR_PLUS;
6298       break;
6299
6300     case 0x1647:        // (blue steel)
6301       element = EL_STEEL_CHAR_MINUS;
6302       break;
6303
6304     case 0x1648:        // (blue steel)
6305       element = EL_STEEL_CHAR_APOSTROPHE;
6306       break;
6307
6308     case 0x1649:        // (blue steel)
6309       element = EL_STEEL_CHAR_PARENLEFT;
6310       break;
6311
6312     case 0x164a:        // (blue steel)
6313       element = EL_STEEL_CHAR_PARENRIGHT;
6314       break;
6315
6316     case 0x164b:        // (green steel)
6317       element = EL_STEEL_CHAR_A;
6318       break;
6319
6320     case 0x164c:        // (green steel)
6321       element = EL_STEEL_CHAR_B;
6322       break;
6323
6324     case 0x164d:        // (green steel)
6325       element = EL_STEEL_CHAR_C;
6326       break;
6327
6328     case 0x164e:        // (green steel)
6329       element = EL_STEEL_CHAR_D;
6330       break;
6331
6332     case 0x164f:        // (green steel)
6333       element = EL_STEEL_CHAR_E;
6334       break;
6335
6336     case 0x1650:        // (green steel)
6337       element = EL_STEEL_CHAR_F;
6338       break;
6339
6340     case 0x1651:        // (green steel)
6341       element = EL_STEEL_CHAR_G;
6342       break;
6343
6344     case 0x1652:        // (green steel)
6345       element = EL_STEEL_CHAR_H;
6346       break;
6347
6348     case 0x1653:        // (green steel)
6349       element = EL_STEEL_CHAR_I;
6350       break;
6351
6352     case 0x1654:        // (green steel)
6353       element = EL_STEEL_CHAR_J;
6354       break;
6355
6356     case 0x1655:        // (green steel)
6357       element = EL_STEEL_CHAR_K;
6358       break;
6359
6360     case 0x1656:        // (green steel)
6361       element = EL_STEEL_CHAR_L;
6362       break;
6363
6364     case 0x1657:        // (green steel)
6365       element = EL_STEEL_CHAR_M;
6366       break;
6367
6368     case 0x1658:        // (green steel)
6369       element = EL_STEEL_CHAR_N;
6370       break;
6371
6372     case 0x1659:        // (green steel)
6373       element = EL_STEEL_CHAR_O;
6374       break;
6375
6376     case 0x165a:        // (green steel)
6377       element = EL_STEEL_CHAR_P;
6378       break;
6379
6380     case 0x165b:        // (green steel)
6381       element = EL_STEEL_CHAR_Q;
6382       break;
6383
6384     case 0x165c:        // (green steel)
6385       element = EL_STEEL_CHAR_R;
6386       break;
6387
6388     case 0x165d:        // (green steel)
6389       element = EL_STEEL_CHAR_S;
6390       break;
6391
6392     case 0x165e:        // (green steel)
6393       element = EL_STEEL_CHAR_T;
6394       break;
6395
6396     case 0x165f:        // (green steel)
6397       element = EL_STEEL_CHAR_U;
6398       break;
6399
6400     case 0x1660:        // (green steel)
6401       element = EL_STEEL_CHAR_V;
6402       break;
6403
6404     case 0x1661:        // (green steel)
6405       element = EL_STEEL_CHAR_W;
6406       break;
6407
6408     case 0x1662:        // (green steel)
6409       element = EL_STEEL_CHAR_X;
6410       break;
6411
6412     case 0x1663:        // (green steel)
6413       element = EL_STEEL_CHAR_Y;
6414       break;
6415
6416     case 0x1664:        // (green steel)
6417       element = EL_STEEL_CHAR_Z;
6418       break;
6419
6420     case 0x1665:        // (green steel)
6421       element = EL_STEEL_CHAR_AUMLAUT;
6422       break;
6423
6424     case 0x1666:        // (green steel)
6425       element = EL_STEEL_CHAR_OUMLAUT;
6426       break;
6427
6428     case 0x1667:        // (green steel)
6429       element = EL_STEEL_CHAR_UUMLAUT;
6430       break;
6431
6432     case 0x1668:        // (green steel)
6433       element = EL_STEEL_CHAR_0;
6434       break;
6435
6436     case 0x1669:        // (green steel)
6437       element = EL_STEEL_CHAR_1;
6438       break;
6439
6440     case 0x166a:        // (green steel)
6441       element = EL_STEEL_CHAR_2;
6442       break;
6443
6444     case 0x166b:        // (green steel)
6445       element = EL_STEEL_CHAR_3;
6446       break;
6447
6448     case 0x166c:        // (green steel)
6449       element = EL_STEEL_CHAR_4;
6450       break;
6451
6452     case 0x166d:        // (green steel)
6453       element = EL_STEEL_CHAR_5;
6454       break;
6455
6456     case 0x166e:        // (green steel)
6457       element = EL_STEEL_CHAR_6;
6458       break;
6459
6460     case 0x166f:        // (green steel)
6461       element = EL_STEEL_CHAR_7;
6462       break;
6463
6464     case 0x1670:        // (green steel)
6465       element = EL_STEEL_CHAR_8;
6466       break;
6467
6468     case 0x1671:        // (green steel)
6469       element = EL_STEEL_CHAR_9;
6470       break;
6471
6472     case 0x1672:        // (green steel)
6473       element = EL_STEEL_CHAR_PERIOD;
6474       break;
6475
6476     case 0x1673:        // (green steel)
6477       element = EL_STEEL_CHAR_EXCLAM;
6478       break;
6479
6480     case 0x1674:        // (green steel)
6481       element = EL_STEEL_CHAR_COLON;
6482       break;
6483
6484     case 0x1675:        // (green steel)
6485       element = EL_STEEL_CHAR_LESS;
6486       break;
6487
6488     case 0x1676:        // (green steel)
6489       element = EL_STEEL_CHAR_GREATER;
6490       break;
6491
6492     case 0x1677:        // (green steel)
6493       element = EL_STEEL_CHAR_QUESTION;
6494       break;
6495
6496     case 0x1678:        // (green steel)
6497       element = EL_STEEL_CHAR_COPYRIGHT;
6498       break;
6499
6500     case 0x1679:        // (green steel)
6501       element = EL_STEEL_CHAR_UP;
6502       break;
6503
6504     case 0x167a:        // (green steel)
6505       element = EL_STEEL_CHAR_DOWN;
6506       break;
6507
6508     case 0x167b:        // (green steel)
6509       element = EL_STEEL_CHAR_BUTTON;
6510       break;
6511
6512     case 0x167c:        // (green steel)
6513       element = EL_STEEL_CHAR_PLUS;
6514       break;
6515
6516     case 0x167d:        // (green steel)
6517       element = EL_STEEL_CHAR_MINUS;
6518       break;
6519
6520     case 0x167e:        // (green steel)
6521       element = EL_STEEL_CHAR_APOSTROPHE;
6522       break;
6523
6524     case 0x167f:        // (green steel)
6525       element = EL_STEEL_CHAR_PARENLEFT;
6526       break;
6527
6528     case 0x1680:        // (green steel)
6529       element = EL_STEEL_CHAR_PARENRIGHT;
6530       break;
6531
6532     case 0x1681:        // gate (red)
6533       element = EL_EM_GATE_1;
6534       break;
6535
6536     case 0x1682:        // secret gate (red)
6537       element = EL_EM_GATE_1_GRAY;
6538       break;
6539
6540     case 0x1683:        // gate (yellow)
6541       element = EL_EM_GATE_2;
6542       break;
6543
6544     case 0x1684:        // secret gate (yellow)
6545       element = EL_EM_GATE_2_GRAY;
6546       break;
6547
6548     case 0x1685:        // gate (blue)
6549       element = EL_EM_GATE_4;
6550       break;
6551
6552     case 0x1686:        // secret gate (blue)
6553       element = EL_EM_GATE_4_GRAY;
6554       break;
6555
6556     case 0x1687:        // gate (green)
6557       element = EL_EM_GATE_3;
6558       break;
6559
6560     case 0x1688:        // secret gate (green)
6561       element = EL_EM_GATE_3_GRAY;
6562       break;
6563
6564     case 0x1689:        // gate (white)
6565       element = EL_DC_GATE_WHITE;
6566       break;
6567
6568     case 0x168a:        // secret gate (white)
6569       element = EL_DC_GATE_WHITE_GRAY;
6570       break;
6571
6572     case 0x168b:        // secret gate (no key)
6573       element = EL_DC_GATE_FAKE_GRAY;
6574       break;
6575
6576     case 0x168c:
6577       element = EL_ROBOT_WHEEL;
6578       break;
6579
6580     case 0x168d:
6581       element = EL_DC_TIMEGATE_SWITCH;
6582       break;
6583
6584     case 0x168e:
6585       element = EL_ACID_POOL_BOTTOM;
6586       break;
6587
6588     case 0x168f:
6589       element = EL_ACID_POOL_TOPLEFT;
6590       break;
6591
6592     case 0x1690:
6593       element = EL_ACID_POOL_TOPRIGHT;
6594       break;
6595
6596     case 0x1691:
6597       element = EL_ACID_POOL_BOTTOMLEFT;
6598       break;
6599
6600     case 0x1692:
6601       element = EL_ACID_POOL_BOTTOMRIGHT;
6602       break;
6603
6604     case 0x1693:
6605       element = EL_STEELWALL;
6606       break;
6607
6608     case 0x1694:
6609       element = EL_STEELWALL_SLIPPERY;
6610       break;
6611
6612     case 0x1695:        // steel wall (not round)
6613       element = EL_STEELWALL;
6614       break;
6615
6616     case 0x1696:        // steel wall (left)
6617       element = EL_DC_STEELWALL_1_LEFT;
6618       break;
6619
6620     case 0x1697:        // steel wall (bottom)
6621       element = EL_DC_STEELWALL_1_BOTTOM;
6622       break;
6623
6624     case 0x1698:        // steel wall (right)
6625       element = EL_DC_STEELWALL_1_RIGHT;
6626       break;
6627
6628     case 0x1699:        // steel wall (top)
6629       element = EL_DC_STEELWALL_1_TOP;
6630       break;
6631
6632     case 0x169a:        // steel wall (left/bottom)
6633       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6634       break;
6635
6636     case 0x169b:        // steel wall (right/bottom)
6637       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6638       break;
6639
6640     case 0x169c:        // steel wall (right/top)
6641       element = EL_DC_STEELWALL_1_TOPRIGHT;
6642       break;
6643
6644     case 0x169d:        // steel wall (left/top)
6645       element = EL_DC_STEELWALL_1_TOPLEFT;
6646       break;
6647
6648     case 0x169e:        // steel wall (right/bottom small)
6649       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6650       break;
6651
6652     case 0x169f:        // steel wall (left/bottom small)
6653       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6654       break;
6655
6656     case 0x16a0:        // steel wall (right/top small)
6657       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6658       break;
6659
6660     case 0x16a1:        // steel wall (left/top small)
6661       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6662       break;
6663
6664     case 0x16a2:        // steel wall (left/right)
6665       element = EL_DC_STEELWALL_1_VERTICAL;
6666       break;
6667
6668     case 0x16a3:        // steel wall (top/bottom)
6669       element = EL_DC_STEELWALL_1_HORIZONTAL;
6670       break;
6671
6672     case 0x16a4:        // steel wall 2 (left end)
6673       element = EL_DC_STEELWALL_2_LEFT;
6674       break;
6675
6676     case 0x16a5:        // steel wall 2 (right end)
6677       element = EL_DC_STEELWALL_2_RIGHT;
6678       break;
6679
6680     case 0x16a6:        // steel wall 2 (top end)
6681       element = EL_DC_STEELWALL_2_TOP;
6682       break;
6683
6684     case 0x16a7:        // steel wall 2 (bottom end)
6685       element = EL_DC_STEELWALL_2_BOTTOM;
6686       break;
6687
6688     case 0x16a8:        // steel wall 2 (left/right)
6689       element = EL_DC_STEELWALL_2_HORIZONTAL;
6690       break;
6691
6692     case 0x16a9:        // steel wall 2 (up/down)
6693       element = EL_DC_STEELWALL_2_VERTICAL;
6694       break;
6695
6696     case 0x16aa:        // steel wall 2 (mid)
6697       element = EL_DC_STEELWALL_2_MIDDLE;
6698       break;
6699
6700     case 0x16ab:
6701       element = EL_SIGN_EXCLAMATION;
6702       break;
6703
6704     case 0x16ac:
6705       element = EL_SIGN_RADIOACTIVITY;
6706       break;
6707
6708     case 0x16ad:
6709       element = EL_SIGN_STOP;
6710       break;
6711
6712     case 0x16ae:
6713       element = EL_SIGN_WHEELCHAIR;
6714       break;
6715
6716     case 0x16af:
6717       element = EL_SIGN_PARKING;
6718       break;
6719
6720     case 0x16b0:
6721       element = EL_SIGN_NO_ENTRY;
6722       break;
6723
6724     case 0x16b1:
6725       element = EL_SIGN_HEART;
6726       break;
6727
6728     case 0x16b2:
6729       element = EL_SIGN_GIVE_WAY;
6730       break;
6731
6732     case 0x16b3:
6733       element = EL_SIGN_ENTRY_FORBIDDEN;
6734       break;
6735
6736     case 0x16b4:
6737       element = EL_SIGN_EMERGENCY_EXIT;
6738       break;
6739
6740     case 0x16b5:
6741       element = EL_SIGN_YIN_YANG;
6742       break;
6743
6744     case 0x16b6:
6745       element = EL_WALL_EMERALD;
6746       break;
6747
6748     case 0x16b7:
6749       element = EL_WALL_DIAMOND;
6750       break;
6751
6752     case 0x16b8:
6753       element = EL_WALL_PEARL;
6754       break;
6755
6756     case 0x16b9:
6757       element = EL_WALL_CRYSTAL;
6758       break;
6759
6760     case 0x16ba:
6761       element = EL_INVISIBLE_WALL;
6762       break;
6763
6764     case 0x16bb:
6765       element = EL_INVISIBLE_STEELWALL;
6766       break;
6767
6768       // 0x16bc - 0x16cb:
6769       // EL_INVISIBLE_SAND
6770
6771     case 0x16cc:
6772       element = EL_LIGHT_SWITCH;
6773       break;
6774
6775     case 0x16cd:
6776       element = EL_ENVELOPE_1;
6777       break;
6778
6779     default:
6780       if (element >= 0x0117 && element <= 0x036e)       // (?)
6781         element = EL_DIAMOND;
6782       else if (element >= 0x042d && element <= 0x0684)  // (?)
6783         element = EL_EMERALD;
6784       else if (element >= 0x157c && element <= 0x158b)
6785         element = EL_SAND;
6786       else if (element >= 0x1590 && element <= 0x159f)
6787         element = EL_DC_LANDMINE;
6788       else if (element >= 0x16bc && element <= 0x16cb)
6789         element = EL_INVISIBLE_SAND;
6790       else
6791       {
6792         Warn("unknown Diamond Caves element 0x%04x", element);
6793
6794         element = EL_UNKNOWN;
6795       }
6796       break;
6797   }
6798
6799   return getMappedElement(element);
6800 }
6801
6802 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6803 {
6804   byte header[DC_LEVEL_HEADER_SIZE];
6805   int envelope_size;
6806   int envelope_header_pos = 62;
6807   int envelope_content_pos = 94;
6808   int level_name_pos = 251;
6809   int level_author_pos = 292;
6810   int envelope_header_len;
6811   int envelope_content_len;
6812   int level_name_len;
6813   int level_author_len;
6814   int fieldx, fieldy;
6815   int num_yamyam_contents;
6816   int i, x, y;
6817
6818   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6819
6820   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6821   {
6822     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6823
6824     header[i * 2 + 0] = header_word >> 8;
6825     header[i * 2 + 1] = header_word & 0xff;
6826   }
6827
6828   // read some values from level header to check level decoding integrity
6829   fieldx = header[6] | (header[7] << 8);
6830   fieldy = header[8] | (header[9] << 8);
6831   num_yamyam_contents = header[60] | (header[61] << 8);
6832
6833   // do some simple sanity checks to ensure that level was correctly decoded
6834   if (fieldx < 1 || fieldx > 256 ||
6835       fieldy < 1 || fieldy > 256 ||
6836       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6837   {
6838     level->no_valid_file = TRUE;
6839
6840     Warn("cannot decode level from stream -- using empty level");
6841
6842     return;
6843   }
6844
6845   // maximum envelope header size is 31 bytes
6846   envelope_header_len   = header[envelope_header_pos];
6847   // maximum envelope content size is 110 (156?) bytes
6848   envelope_content_len  = header[envelope_content_pos];
6849
6850   // maximum level title size is 40 bytes
6851   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6852   // maximum level author size is 30 (51?) bytes
6853   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6854
6855   envelope_size = 0;
6856
6857   for (i = 0; i < envelope_header_len; i++)
6858     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6859       level->envelope[0].text[envelope_size++] =
6860         header[envelope_header_pos + 1 + i];
6861
6862   if (envelope_header_len > 0 && envelope_content_len > 0)
6863   {
6864     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6865       level->envelope[0].text[envelope_size++] = '\n';
6866     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6867       level->envelope[0].text[envelope_size++] = '\n';
6868   }
6869
6870   for (i = 0; i < envelope_content_len; i++)
6871     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6872       level->envelope[0].text[envelope_size++] =
6873         header[envelope_content_pos + 1 + i];
6874
6875   level->envelope[0].text[envelope_size] = '\0';
6876
6877   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6878   level->envelope[0].ysize = 10;
6879   level->envelope[0].autowrap = TRUE;
6880   level->envelope[0].centered = TRUE;
6881
6882   for (i = 0; i < level_name_len; i++)
6883     level->name[i] = header[level_name_pos + 1 + i];
6884   level->name[level_name_len] = '\0';
6885
6886   for (i = 0; i < level_author_len; i++)
6887     level->author[i] = header[level_author_pos + 1 + i];
6888   level->author[level_author_len] = '\0';
6889
6890   num_yamyam_contents = header[60] | (header[61] << 8);
6891   level->num_yamyam_contents =
6892     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6893
6894   for (i = 0; i < num_yamyam_contents; i++)
6895   {
6896     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6897     {
6898       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6899       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6900
6901       if (i < MAX_ELEMENT_CONTENTS)
6902         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6903     }
6904   }
6905
6906   fieldx = header[6] | (header[7] << 8);
6907   fieldy = header[8] | (header[9] << 8);
6908   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6909   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6910
6911   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6912   {
6913     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6914     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6915
6916     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6917       level->field[x][y] = getMappedElement_DC(element_dc);
6918   }
6919
6920   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6921   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6922   level->field[x][y] = EL_PLAYER_1;
6923
6924   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6925   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6926   level->field[x][y] = EL_PLAYER_2;
6927
6928   level->gems_needed            = header[18] | (header[19] << 8);
6929
6930   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6931   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6932   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6933   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6934   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6935   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6936   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6937   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6938   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6939   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6940   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6941   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6942
6943   level->time                   = header[44] | (header[45] << 8);
6944
6945   level->amoeba_speed           = header[46] | (header[47] << 8);
6946   level->time_light             = header[48] | (header[49] << 8);
6947   level->time_timegate          = header[50] | (header[51] << 8);
6948   level->time_wheel             = header[52] | (header[53] << 8);
6949   level->time_magic_wall        = header[54] | (header[55] << 8);
6950   level->extra_time             = header[56] | (header[57] << 8);
6951   level->shield_normal_time     = header[58] | (header[59] << 8);
6952
6953   // shield and extra time elements do not have a score
6954   level->score[SC_SHIELD]       = 0;
6955   level->extra_time_score       = 0;
6956
6957   // set time for normal and deadly shields to the same value
6958   level->shield_deadly_time     = level->shield_normal_time;
6959
6960   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6961   // can slip down from flat walls, like normal walls and steel walls
6962   level->em_slippery_gems = TRUE;
6963
6964   // time score is counted for each 10 seconds left in Diamond Caves levels
6965   level->time_score_base = 10;
6966 }
6967
6968 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6969                                      struct LevelFileInfo *level_file_info,
6970                                      boolean level_info_only)
6971 {
6972   char *filename = level_file_info->filename;
6973   File *file;
6974   int num_magic_bytes = 8;
6975   char magic_bytes[num_magic_bytes + 1];
6976   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6977
6978   if (!(file = openFile(filename, MODE_READ)))
6979   {
6980     level->no_valid_file = TRUE;
6981
6982     if (!level_info_only)
6983       Warn("cannot read level '%s' -- using empty level", filename);
6984
6985     return;
6986   }
6987
6988   // fseek(file, 0x0000, SEEK_SET);
6989
6990   if (level_file_info->packed)
6991   {
6992     // read "magic bytes" from start of file
6993     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6994       magic_bytes[0] = '\0';
6995
6996     // check "magic bytes" for correct file format
6997     if (!strPrefix(magic_bytes, "DC2"))
6998     {
6999       level->no_valid_file = TRUE;
7000
7001       Warn("unknown DC level file '%s' -- using empty level", filename);
7002
7003       return;
7004     }
7005
7006     if (strPrefix(magic_bytes, "DC2Win95") ||
7007         strPrefix(magic_bytes, "DC2Win98"))
7008     {
7009       int position_first_level = 0x00fa;
7010       int extra_bytes = 4;
7011       int skip_bytes;
7012
7013       // advance file stream to first level inside the level package
7014       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
7015
7016       // each block of level data is followed by block of non-level data
7017       num_levels_to_skip *= 2;
7018
7019       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7020       while (num_levels_to_skip >= 0)
7021       {
7022         // advance file stream to next level inside the level package
7023         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7024         {
7025           level->no_valid_file = TRUE;
7026
7027           Warn("cannot fseek in file '%s' -- using empty level", filename);
7028
7029           return;
7030         }
7031
7032         // skip apparently unused extra bytes following each level
7033         ReadUnusedBytesFromFile(file, extra_bytes);
7034
7035         // read size of next level in level package
7036         skip_bytes = getFile32BitLE(file);
7037
7038         num_levels_to_skip--;
7039       }
7040     }
7041     else
7042     {
7043       level->no_valid_file = TRUE;
7044
7045       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7046
7047       return;
7048     }
7049   }
7050
7051   LoadLevelFromFileStream_DC(file, level);
7052
7053   closeFile(file);
7054 }
7055
7056
7057 // ----------------------------------------------------------------------------
7058 // functions for loading SB level
7059 // ----------------------------------------------------------------------------
7060
7061 int getMappedElement_SB(int element_ascii, boolean use_ces)
7062 {
7063   static struct
7064   {
7065     int ascii;
7066     int sb;
7067     int ce;
7068   }
7069   sb_element_mapping[] =
7070   {
7071     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7072     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7073     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7074     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7075     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7076     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7077     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7078     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7079
7080     { 0,   -1,                      -1          },
7081   };
7082
7083   int i;
7084
7085   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7086     if (element_ascii == sb_element_mapping[i].ascii)
7087       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7088
7089   return EL_UNDEFINED;
7090 }
7091
7092 static void SetLevelSettings_SB(struct LevelInfo *level)
7093 {
7094   // time settings
7095   level->time = 0;
7096   level->use_step_counter = TRUE;
7097
7098   // score settings
7099   level->score[SC_TIME_BONUS] = 0;
7100   level->time_score_base = 1;
7101   level->rate_time_over_score = TRUE;
7102
7103   // game settings
7104   level->auto_exit_sokoban = TRUE;
7105 }
7106
7107 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7108                                      struct LevelFileInfo *level_file_info,
7109                                      boolean level_info_only)
7110 {
7111   char *filename = level_file_info->filename;
7112   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7113   char last_comment[MAX_LINE_LEN];
7114   char level_name[MAX_LINE_LEN];
7115   char *line_ptr;
7116   File *file;
7117   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7118   boolean read_continued_line = FALSE;
7119   boolean reading_playfield = FALSE;
7120   boolean got_valid_playfield_line = FALSE;
7121   boolean invalid_playfield_char = FALSE;
7122   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7123   int file_level_nr = 0;
7124   int x = 0, y = 0;             // initialized to make compilers happy
7125
7126   last_comment[0] = '\0';
7127   level_name[0] = '\0';
7128
7129   if (!(file = openFile(filename, MODE_READ)))
7130   {
7131     level->no_valid_file = TRUE;
7132
7133     if (!level_info_only)
7134       Warn("cannot read level '%s' -- using empty level", filename);
7135
7136     return;
7137   }
7138
7139   while (!checkEndOfFile(file))
7140   {
7141     // level successfully read, but next level may follow here
7142     if (!got_valid_playfield_line && reading_playfield)
7143     {
7144       // read playfield from single level file -- skip remaining file
7145       if (!level_file_info->packed)
7146         break;
7147
7148       if (file_level_nr >= num_levels_to_skip)
7149         break;
7150
7151       file_level_nr++;
7152
7153       last_comment[0] = '\0';
7154       level_name[0] = '\0';
7155
7156       reading_playfield = FALSE;
7157     }
7158
7159     got_valid_playfield_line = FALSE;
7160
7161     // read next line of input file
7162     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7163       break;
7164
7165     // cut trailing line break (this can be newline and/or carriage return)
7166     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7167       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7168         *line_ptr = '\0';
7169
7170     // copy raw input line for later use (mainly debugging output)
7171     strcpy(line_raw, line);
7172
7173     if (read_continued_line)
7174     {
7175       // append new line to existing line, if there is enough space
7176       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7177         strcat(previous_line, line_ptr);
7178
7179       strcpy(line, previous_line);      // copy storage buffer to line
7180
7181       read_continued_line = FALSE;
7182     }
7183
7184     // if the last character is '\', continue at next line
7185     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7186     {
7187       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7188       strcpy(previous_line, line);      // copy line to storage buffer
7189
7190       read_continued_line = TRUE;
7191
7192       continue;
7193     }
7194
7195     // skip empty lines
7196     if (line[0] == '\0')
7197       continue;
7198
7199     // extract comment text from comment line
7200     if (line[0] == ';')
7201     {
7202       for (line_ptr = line; *line_ptr; line_ptr++)
7203         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7204           break;
7205
7206       strcpy(last_comment, line_ptr);
7207
7208       continue;
7209     }
7210
7211     // extract level title text from line containing level title
7212     if (line[0] == '\'')
7213     {
7214       strcpy(level_name, &line[1]);
7215
7216       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7217         level_name[strlen(level_name) - 1] = '\0';
7218
7219       continue;
7220     }
7221
7222     // skip lines containing only spaces (or empty lines)
7223     for (line_ptr = line; *line_ptr; line_ptr++)
7224       if (*line_ptr != ' ')
7225         break;
7226     if (*line_ptr == '\0')
7227       continue;
7228
7229     // at this point, we have found a line containing part of a playfield
7230
7231     got_valid_playfield_line = TRUE;
7232
7233     if (!reading_playfield)
7234     {
7235       reading_playfield = TRUE;
7236       invalid_playfield_char = FALSE;
7237
7238       for (x = 0; x < MAX_LEV_FIELDX; x++)
7239         for (y = 0; y < MAX_LEV_FIELDY; y++)
7240           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7241
7242       level->fieldx = 0;
7243       level->fieldy = 0;
7244
7245       // start with topmost tile row
7246       y = 0;
7247     }
7248
7249     // skip playfield line if larger row than allowed
7250     if (y >= MAX_LEV_FIELDY)
7251       continue;
7252
7253     // start with leftmost tile column
7254     x = 0;
7255
7256     // read playfield elements from line
7257     for (line_ptr = line; *line_ptr; line_ptr++)
7258     {
7259       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7260
7261       // stop parsing playfield line if larger column than allowed
7262       if (x >= MAX_LEV_FIELDX)
7263         break;
7264
7265       if (mapped_sb_element == EL_UNDEFINED)
7266       {
7267         invalid_playfield_char = TRUE;
7268
7269         break;
7270       }
7271
7272       level->field[x][y] = mapped_sb_element;
7273
7274       // continue with next tile column
7275       x++;
7276
7277       level->fieldx = MAX(x, level->fieldx);
7278     }
7279
7280     if (invalid_playfield_char)
7281     {
7282       // if first playfield line, treat invalid lines as comment lines
7283       if (y == 0)
7284         reading_playfield = FALSE;
7285
7286       continue;
7287     }
7288
7289     // continue with next tile row
7290     y++;
7291   }
7292
7293   closeFile(file);
7294
7295   level->fieldy = y;
7296
7297   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7298   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7299
7300   if (!reading_playfield)
7301   {
7302     level->no_valid_file = TRUE;
7303
7304     Warn("cannot read level '%s' -- using empty level", filename);
7305
7306     return;
7307   }
7308
7309   if (*level_name != '\0')
7310   {
7311     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7312     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7313   }
7314   else if (*last_comment != '\0')
7315   {
7316     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7317     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7318   }
7319   else
7320   {
7321     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7322   }
7323
7324   // set all empty fields beyond the border walls to invisible steel wall
7325   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7326   {
7327     if ((x == 0 || x == level->fieldx - 1 ||
7328          y == 0 || y == level->fieldy - 1) &&
7329         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7330       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7331                      level->field, level->fieldx, level->fieldy);
7332   }
7333
7334   // set special level settings for Sokoban levels
7335   SetLevelSettings_SB(level);
7336
7337   if (load_xsb_to_ces)
7338   {
7339     // special global settings can now be set in level template
7340     level->use_custom_template = TRUE;
7341   }
7342 }
7343
7344
7345 // -------------------------------------------------------------------------
7346 // functions for handling native levels
7347 // -------------------------------------------------------------------------
7348
7349 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7350                                      struct LevelFileInfo *level_file_info,
7351                                      boolean level_info_only)
7352 {
7353   int pos = 0;
7354
7355   // determine position of requested level inside level package
7356   if (level_file_info->packed)
7357     pos = level_file_info->nr - leveldir_current->first_level;
7358
7359   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7360     level->no_valid_file = TRUE;
7361 }
7362
7363 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7364                                      struct LevelFileInfo *level_file_info,
7365                                      boolean level_info_only)
7366 {
7367   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7368     level->no_valid_file = TRUE;
7369 }
7370
7371 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7372                                      struct LevelFileInfo *level_file_info,
7373                                      boolean level_info_only)
7374 {
7375   int pos = 0;
7376
7377   // determine position of requested level inside level package
7378   if (level_file_info->packed)
7379     pos = level_file_info->nr - leveldir_current->first_level;
7380
7381   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7382     level->no_valid_file = TRUE;
7383 }
7384
7385 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7386                                      struct LevelFileInfo *level_file_info,
7387                                      boolean level_info_only)
7388 {
7389   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7390     level->no_valid_file = TRUE;
7391 }
7392
7393 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7394 {
7395   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7396     CopyNativeLevel_RND_to_BD(level);
7397   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7398     CopyNativeLevel_RND_to_EM(level);
7399   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7400     CopyNativeLevel_RND_to_SP(level);
7401   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7402     CopyNativeLevel_RND_to_MM(level);
7403 }
7404
7405 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7406 {
7407   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7408     CopyNativeLevel_BD_to_RND(level);
7409   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7410     CopyNativeLevel_EM_to_RND(level);
7411   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7412     CopyNativeLevel_SP_to_RND(level);
7413   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7414     CopyNativeLevel_MM_to_RND(level);
7415 }
7416
7417 void SaveNativeLevel(struct LevelInfo *level)
7418 {
7419   // saving native level files only supported for some game engines
7420   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7421       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7422     return;
7423
7424   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7425                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7426   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7427   char *filename = getLevelFilenameFromBasename(basename);
7428
7429   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7430     return;
7431
7432   boolean success = FALSE;
7433
7434   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7435   {
7436     CopyNativeLevel_RND_to_BD(level);
7437     // CopyNativeTape_RND_to_BD(level);
7438
7439     success = SaveNativeLevel_BD(filename);
7440   }
7441   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7442   {
7443     CopyNativeLevel_RND_to_SP(level);
7444     CopyNativeTape_RND_to_SP(level);
7445
7446     success = SaveNativeLevel_SP(filename);
7447   }
7448
7449   if (success)
7450     Request("Native level file saved!", REQ_CONFIRM);
7451   else
7452     Request("Failed to save native level file!", REQ_CONFIRM);
7453 }
7454
7455
7456 // ----------------------------------------------------------------------------
7457 // functions for loading generic level
7458 // ----------------------------------------------------------------------------
7459
7460 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7461                                   struct LevelFileInfo *level_file_info,
7462                                   boolean level_info_only)
7463 {
7464   // always start with reliable default values
7465   setLevelInfoToDefaults(level, level_info_only, TRUE);
7466
7467   switch (level_file_info->type)
7468   {
7469     case LEVEL_FILE_TYPE_RND:
7470       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7471       break;
7472
7473     case LEVEL_FILE_TYPE_BD:
7474       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7475       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7476       break;
7477
7478     case LEVEL_FILE_TYPE_EM:
7479       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7480       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7481       break;
7482
7483     case LEVEL_FILE_TYPE_SP:
7484       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7485       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7486       break;
7487
7488     case LEVEL_FILE_TYPE_MM:
7489       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7490       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7491       break;
7492
7493     case LEVEL_FILE_TYPE_DC:
7494       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7495       break;
7496
7497     case LEVEL_FILE_TYPE_SB:
7498       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7499       break;
7500
7501     default:
7502       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7503       break;
7504   }
7505
7506   // if level file is invalid, restore level structure to default values
7507   if (level->no_valid_file)
7508     setLevelInfoToDefaults(level, level_info_only, FALSE);
7509
7510   if (check_special_flags("use_native_bd_game_engine"))
7511     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7512
7513   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7514     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7515
7516   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7517     CopyNativeLevel_Native_to_RND(level);
7518 }
7519
7520 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7521 {
7522   static struct LevelFileInfo level_file_info;
7523
7524   // always start with reliable default values
7525   setFileInfoToDefaults(&level_file_info);
7526
7527   level_file_info.nr = 0;                       // unknown level number
7528   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7529
7530   setString(&level_file_info.filename, filename);
7531
7532   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7533 }
7534
7535 static void LoadLevel_FixEnvelopes(struct LevelInfo *level, boolean skip_single_lines)
7536 {
7537   // This function removes newlines in envelopes after lines of text ending in the last column
7538   // of the envelope. In earlier versions, these newlines were removed when displaying envelopes,
7539   // but caused trouble in the level editor. In version 4.3.2.3, this problem was partially
7540   // fixed in the level editor (but only for single full-width text lines followed by a newline,
7541   // not for multiple lines ending in the last column, followed by a newline), but now produced
7542   // unwanted newlines in the game for envelopes stored by previous game versions, which was not
7543   // intended by the level author (and sometimes caused text lines not being displayed anymore at
7544   // the bottom of the envelope).
7545   //
7546   // This function should solve these problems by removing such newline characters from envelopes
7547   // stored by older game versions.
7548
7549   int envelope_nr;
7550
7551   for (envelope_nr = 0; envelope_nr < NUM_ENVELOPES; envelope_nr++)
7552   {
7553     char *envelope_ptr = level->envelope[envelope_nr].text;
7554     int envelope_xsize = level->envelope[envelope_nr].xsize;
7555     int envelope_size = strlen(envelope_ptr);
7556     int start = 0;
7557     int i;
7558
7559     for (i = 0; i < envelope_size; i++)
7560     {
7561       // check for newlines in envelope
7562       if (envelope_ptr[i] == '\n')
7563       {
7564         int line_length = i - start;
7565
7566         // check for (non-empty) lines that are a multiple of the envelope width,
7567         // causing a line break inside the envelope (text area in editor and in game)
7568         if (line_length > 0 && line_length % envelope_xsize == 0)
7569         {
7570           // special case: skip fixing single lines for newer versions
7571           boolean skip_fixing_line = (line_length == 1 && skip_single_lines);
7572
7573           if (!skip_fixing_line)
7574           {
7575             int j;
7576
7577             // remove newline character from string
7578             for (j = i; j < envelope_size; j++)
7579               envelope_ptr[j] = envelope_ptr[j + 1];
7580           }
7581
7582           // continue with next line (that was copied over the newline)
7583           start = i;
7584         }
7585         else
7586         {
7587           // continue with next character after newline
7588           start = i + 1;
7589         }
7590       }
7591     }
7592   }
7593 }
7594
7595 static void LoadLevel_InitVersion(struct LevelInfo *level)
7596 {
7597   int i, j;
7598
7599   if (leveldir_current == NULL)         // only when dumping level
7600     return;
7601
7602   // all engine modifications also valid for levels which use latest engine
7603   if (level->game_version < VERSION_IDENT(3,2,0,5))
7604   {
7605     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7606     level->time_score_base = 10;
7607   }
7608
7609   if (leveldir_current->latest_engine)
7610   {
7611     // ---------- use latest game engine --------------------------------------
7612
7613     /* For all levels which are forced to use the latest game engine version
7614        (normally all but user contributed, private and undefined levels), set
7615        the game engine version to the actual version; this allows for actual
7616        corrections in the game engine to take effect for existing, converted
7617        levels (from "classic" or other existing games) to make the emulation
7618        of the corresponding game more accurate, while (hopefully) not breaking
7619        existing levels created from other players. */
7620
7621     level->game_version = GAME_VERSION_ACTUAL;
7622
7623     /* Set special EM style gems behaviour: EM style gems slip down from
7624        normal, steel and growing wall. As this is a more fundamental change,
7625        it seems better to set the default behaviour to "off" (as it is more
7626        natural) and make it configurable in the level editor (as a property
7627        of gem style elements). Already existing converted levels (neither
7628        private nor contributed levels) are changed to the new behaviour. */
7629
7630     if (level->file_version < FILE_VERSION_2_0)
7631       level->em_slippery_gems = TRUE;
7632
7633     return;
7634   }
7635
7636   // ---------- use game engine the level was created with --------------------
7637
7638   /* For all levels which are not forced to use the latest game engine
7639      version (normally user contributed, private and undefined levels),
7640      use the version of the game engine the levels were created for.
7641
7642      Since 2.0.1, the game engine version is now directly stored
7643      in the level file (chunk "VERS"), so there is no need anymore
7644      to set the game version from the file version (except for old,
7645      pre-2.0 levels, where the game version is still taken from the
7646      file format version used to store the level -- see above). */
7647
7648   // player was faster than enemies in 1.0.0 and before
7649   if (level->file_version == FILE_VERSION_1_0)
7650     for (i = 0; i < MAX_PLAYERS; i++)
7651       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7652
7653   // default behaviour for EM style gems was "slippery" only in 2.0.1
7654   if (level->game_version == VERSION_IDENT(2,0,1,0))
7655     level->em_slippery_gems = TRUE;
7656
7657   // springs could be pushed over pits before (pre-release version) 2.2.0
7658   if (level->game_version < VERSION_IDENT(2,2,0,0))
7659     level->use_spring_bug = TRUE;
7660
7661   if (level->game_version < VERSION_IDENT(3,2,0,5))
7662   {
7663     // time orb caused limited time in endless time levels before 3.2.0-5
7664     level->use_time_orb_bug = TRUE;
7665
7666     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7667     level->block_snap_field = FALSE;
7668
7669     // extra time score was same value as time left score before 3.2.0-5
7670     level->extra_time_score = level->score[SC_TIME_BONUS];
7671   }
7672
7673   if (level->game_version < VERSION_IDENT(3,2,0,7))
7674   {
7675     // default behaviour for snapping was "not continuous" before 3.2.0-7
7676     level->continuous_snapping = FALSE;
7677   }
7678
7679   // only few elements were able to actively move into acid before 3.1.0
7680   // trigger settings did not exist before 3.1.0; set to default "any"
7681   if (level->game_version < VERSION_IDENT(3,1,0,0))
7682   {
7683     // correct "can move into acid" settings (all zero in old levels)
7684
7685     level->can_move_into_acid_bits = 0; // nothing can move into acid
7686     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7687
7688     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7689     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7690     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7691     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7692
7693     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7694       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7695
7696     // correct trigger settings (stored as zero == "none" in old levels)
7697
7698     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7699     {
7700       int element = EL_CUSTOM_START + i;
7701       struct ElementInfo *ei = &element_info[element];
7702
7703       for (j = 0; j < ei->num_change_pages; j++)
7704       {
7705         struct ElementChangeInfo *change = &ei->change_page[j];
7706
7707         change->trigger_player = CH_PLAYER_ANY;
7708         change->trigger_page = CH_PAGE_ANY;
7709       }
7710     }
7711   }
7712
7713   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7714   {
7715     int element = EL_CUSTOM_256;
7716     struct ElementInfo *ei = &element_info[element];
7717     struct ElementChangeInfo *change = &ei->change_page[0];
7718
7719     /* This is needed to fix a problem that was caused by a bugfix in function
7720        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7721        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7722        not replace walkable elements, but instead just placed the player on it,
7723        without placing the Sokoban field under the player). Unfortunately, this
7724        breaks "Snake Bite" style levels when the snake is halfway through a door
7725        that just closes (the snake head is still alive and can be moved in this
7726        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7727        player (without Sokoban element) which then gets killed as designed). */
7728
7729     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7730          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7731         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7732       change->target_element = EL_PLAYER_1;
7733   }
7734
7735   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7736   if (level->game_version < VERSION_IDENT(3,2,5,0))
7737   {
7738     /* This is needed to fix a problem that was caused by a bugfix in function
7739        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7740        corrects the behaviour when a custom element changes to another custom
7741        element with a higher element number that has change actions defined.
7742        Normally, only one change per frame is allowed for custom elements.
7743        Therefore, it is checked if a custom element already changed in the
7744        current frame; if it did, subsequent changes are suppressed.
7745        Unfortunately, this is only checked for element changes, but not for
7746        change actions, which are still executed. As the function above loops
7747        through all custom elements from lower to higher, an element change
7748        resulting in a lower CE number won't be checked again, while a target
7749        element with a higher number will also be checked, and potential change
7750        actions will get executed for this CE, too (which is wrong), while
7751        further changes are ignored (which is correct). As this bugfix breaks
7752        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7753        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7754        behaviour for existing levels and tapes that make use of this bug */
7755
7756     level->use_action_after_change_bug = TRUE;
7757   }
7758
7759   // not centering level after relocating player was default only in 3.2.3
7760   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7761     level->shifted_relocation = TRUE;
7762
7763   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7764   if (level->game_version < VERSION_IDENT(3,2,6,0))
7765     level->em_explodes_by_fire = TRUE;
7766
7767   // levels were solved by the first player entering an exit up to 4.1.0.0
7768   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7769     level->solved_by_one_player = TRUE;
7770
7771   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7772   if (level->game_version < VERSION_IDENT(4,1,1,1))
7773     level->use_life_bugs = TRUE;
7774
7775   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7776   if (level->game_version < VERSION_IDENT(4,1,1,1))
7777     level->sb_objects_needed = FALSE;
7778
7779   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7780   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7781     level->finish_dig_collect = FALSE;
7782
7783   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7784   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7785     level->keep_walkable_ce = TRUE;
7786
7787   // envelopes may contain broken or too many line breaks before 4.4.0.0
7788   if (level->game_version < VERSION_IDENT(4,4,0,0))
7789     LoadLevel_FixEnvelopes(level, (level->game_version >= VERSION_IDENT(4,3,2,3)));
7790 }
7791
7792 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7793 {
7794   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7795   int x, y;
7796
7797   // check if this level is (not) a Sokoban level
7798   for (y = 0; y < level->fieldy; y++)
7799     for (x = 0; x < level->fieldx; x++)
7800       if (!IS_SB_ELEMENT(Tile[x][y]))
7801         is_sokoban_level = FALSE;
7802
7803   if (is_sokoban_level)
7804   {
7805     // set special level settings for Sokoban levels
7806     SetLevelSettings_SB(level);
7807   }
7808 }
7809
7810 static void LoadLevel_InitSettings(struct LevelInfo *level)
7811 {
7812   // adjust level settings for (non-native) Sokoban-style levels
7813   LoadLevel_InitSettings_SB(level);
7814
7815   // rename levels with title "nameless level" or if renaming is forced
7816   if (leveldir_current->empty_level_name != NULL &&
7817       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7818        leveldir_current->force_level_name))
7819     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7820              leveldir_current->empty_level_name, level_nr);
7821 }
7822
7823 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7824 {
7825   int i, x, y;
7826
7827   // map elements that have changed in newer versions
7828   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7829                                                     level->game_version);
7830   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7831     for (x = 0; x < 3; x++)
7832       for (y = 0; y < 3; y++)
7833         level->yamyam_content[i].e[x][y] =
7834           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7835                                     level->game_version);
7836
7837 }
7838
7839 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7840 {
7841   int i, j;
7842
7843   // map custom element change events that have changed in newer versions
7844   // (these following values were accidentally changed in version 3.0.1)
7845   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7846   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7847   {
7848     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7849     {
7850       int element = EL_CUSTOM_START + i;
7851
7852       // order of checking and copying events to be mapped is important
7853       // (do not change the start and end value -- they are constant)
7854       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7855       {
7856         if (HAS_CHANGE_EVENT(element, j - 2))
7857         {
7858           SET_CHANGE_EVENT(element, j - 2, FALSE);
7859           SET_CHANGE_EVENT(element, j, TRUE);
7860         }
7861       }
7862
7863       // order of checking and copying events to be mapped is important
7864       // (do not change the start and end value -- they are constant)
7865       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7866       {
7867         if (HAS_CHANGE_EVENT(element, j - 1))
7868         {
7869           SET_CHANGE_EVENT(element, j - 1, FALSE);
7870           SET_CHANGE_EVENT(element, j, TRUE);
7871         }
7872       }
7873     }
7874   }
7875
7876   // initialize "can_change" field for old levels with only one change page
7877   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7878   {
7879     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7880     {
7881       int element = EL_CUSTOM_START + i;
7882
7883       if (CAN_CHANGE(element))
7884         element_info[element].change->can_change = TRUE;
7885     }
7886   }
7887
7888   // correct custom element values (for old levels without these options)
7889   if (level->game_version < VERSION_IDENT(3,1,1,0))
7890   {
7891     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7892     {
7893       int element = EL_CUSTOM_START + i;
7894       struct ElementInfo *ei = &element_info[element];
7895
7896       if (ei->access_direction == MV_NO_DIRECTION)
7897         ei->access_direction = MV_ALL_DIRECTIONS;
7898     }
7899   }
7900
7901   // correct custom element values (fix invalid values for all versions)
7902   if (1)
7903   {
7904     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7905     {
7906       int element = EL_CUSTOM_START + i;
7907       struct ElementInfo *ei = &element_info[element];
7908
7909       for (j = 0; j < ei->num_change_pages; j++)
7910       {
7911         struct ElementChangeInfo *change = &ei->change_page[j];
7912
7913         if (change->trigger_player == CH_PLAYER_NONE)
7914           change->trigger_player = CH_PLAYER_ANY;
7915
7916         if (change->trigger_side == CH_SIDE_NONE)
7917           change->trigger_side = CH_SIDE_ANY;
7918       }
7919     }
7920   }
7921
7922   // initialize "can_explode" field for old levels which did not store this
7923   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7924   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7925   {
7926     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7927     {
7928       int element = EL_CUSTOM_START + i;
7929
7930       if (EXPLODES_1X1_OLD(element))
7931         element_info[element].explosion_type = EXPLODES_1X1;
7932
7933       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7934                                              EXPLODES_SMASHED(element) ||
7935                                              EXPLODES_IMPACT(element)));
7936     }
7937   }
7938
7939   // correct previously hard-coded move delay values for maze runner style
7940   if (level->game_version < VERSION_IDENT(3,1,1,0))
7941   {
7942     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7943     {
7944       int element = EL_CUSTOM_START + i;
7945
7946       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7947       {
7948         // previously hard-coded and therefore ignored
7949         element_info[element].move_delay_fixed = 9;
7950         element_info[element].move_delay_random = 0;
7951       }
7952     }
7953   }
7954
7955   // set some other uninitialized values of custom elements in older levels
7956   if (level->game_version < VERSION_IDENT(3,1,0,0))
7957   {
7958     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7959     {
7960       int element = EL_CUSTOM_START + i;
7961
7962       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7963
7964       element_info[element].explosion_delay = 17;
7965       element_info[element].ignition_delay = 8;
7966     }
7967   }
7968
7969   // set mouse click change events to work for left/middle/right mouse button
7970   if (level->game_version < VERSION_IDENT(4,2,3,0))
7971   {
7972     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7973     {
7974       int element = EL_CUSTOM_START + i;
7975       struct ElementInfo *ei = &element_info[element];
7976
7977       for (j = 0; j < ei->num_change_pages; j++)
7978       {
7979         struct ElementChangeInfo *change = &ei->change_page[j];
7980
7981         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7982             change->has_event[CE_PRESSED_BY_MOUSE] ||
7983             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7984             change->has_event[CE_MOUSE_PRESSED_ON_X])
7985           change->trigger_side = CH_SIDE_ANY;
7986       }
7987     }
7988   }
7989 }
7990
7991 static void LoadLevel_InitElements(struct LevelInfo *level)
7992 {
7993   LoadLevel_InitStandardElements(level);
7994
7995   if (level->file_has_custom_elements)
7996     LoadLevel_InitCustomElements(level);
7997
7998   // initialize element properties for level editor etc.
7999   InitElementPropertiesEngine(level->game_version);
8000   InitElementPropertiesGfxElement();
8001 }
8002
8003 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
8004 {
8005   int x, y;
8006
8007   // map elements that have changed in newer versions
8008   for (y = 0; y < level->fieldy; y++)
8009     for (x = 0; x < level->fieldx; x++)
8010       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
8011                                                      level->game_version);
8012
8013   // clear unused playfield data (nicer if level gets resized in editor)
8014   for (x = 0; x < MAX_LEV_FIELDX; x++)
8015     for (y = 0; y < MAX_LEV_FIELDY; y++)
8016       if (x >= level->fieldx || y >= level->fieldy)
8017         level->field[x][y] = EL_EMPTY;
8018
8019   // copy elements to runtime playfield array
8020   for (x = 0; x < MAX_LEV_FIELDX; x++)
8021     for (y = 0; y < MAX_LEV_FIELDY; y++)
8022       Tile[x][y] = level->field[x][y];
8023
8024   // initialize level size variables for faster access
8025   lev_fieldx = level->fieldx;
8026   lev_fieldy = level->fieldy;
8027
8028   // determine border element for this level
8029   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
8030     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
8031   else
8032     SetBorderElement();
8033 }
8034
8035 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
8036 {
8037   struct LevelFileInfo *level_file_info = &level->file_info;
8038
8039   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
8040     CopyNativeLevel_RND_to_Native(level);
8041 }
8042
8043 static void LoadLevelTemplate_LoadAndInit(void)
8044 {
8045   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
8046
8047   LoadLevel_InitVersion(&level_template);
8048   LoadLevel_InitElements(&level_template);
8049   LoadLevel_InitSettings(&level_template);
8050
8051   ActivateLevelTemplate();
8052 }
8053
8054 void LoadLevelTemplate(int nr)
8055 {
8056   if (!fileExists(getGlobalLevelTemplateFilename()))
8057   {
8058     Warn("no level template found for this level");
8059
8060     return;
8061   }
8062
8063   setLevelFileInfo(&level_template.file_info, nr);
8064
8065   LoadLevelTemplate_LoadAndInit();
8066 }
8067
8068 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
8069 {
8070   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
8071
8072   LoadLevelTemplate_LoadAndInit();
8073 }
8074
8075 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
8076 {
8077   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
8078
8079   if (level.use_custom_template)
8080   {
8081     if (network_level != NULL)
8082       LoadNetworkLevelTemplate(network_level);
8083     else
8084       LoadLevelTemplate(-1);
8085   }
8086
8087   LoadLevel_InitVersion(&level);
8088   LoadLevel_InitElements(&level);
8089   LoadLevel_InitPlayfield(&level);
8090   LoadLevel_InitSettings(&level);
8091
8092   LoadLevel_InitNativeEngines(&level);
8093 }
8094
8095 void LoadLevel(int nr)
8096 {
8097   SetLevelSetInfo(leveldir_current->identifier, nr);
8098
8099   setLevelFileInfo(&level.file_info, nr);
8100
8101   LoadLevel_LoadAndInit(NULL);
8102 }
8103
8104 void LoadLevelInfoOnly(int nr)
8105 {
8106   setLevelFileInfo(&level.file_info, nr);
8107
8108   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8109 }
8110
8111 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8112 {
8113   SetLevelSetInfo(network_level->leveldir_identifier,
8114                   network_level->file_info.nr);
8115
8116   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8117
8118   LoadLevel_LoadAndInit(network_level);
8119 }
8120
8121 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8122 {
8123   int chunk_size = 0;
8124
8125   chunk_size += putFileVersion(file, level->file_version);
8126   chunk_size += putFileVersion(file, level->game_version);
8127
8128   return chunk_size;
8129 }
8130
8131 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8132 {
8133   int chunk_size = 0;
8134
8135   chunk_size += putFile16BitBE(file, level->creation_date.year);
8136   chunk_size += putFile8Bit(file,    level->creation_date.month);
8137   chunk_size += putFile8Bit(file,    level->creation_date.day);
8138
8139   return chunk_size;
8140 }
8141
8142 #if ENABLE_HISTORIC_CHUNKS
8143 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8144 {
8145   int i, x, y;
8146
8147   putFile8Bit(file, level->fieldx);
8148   putFile8Bit(file, level->fieldy);
8149
8150   putFile16BitBE(file, level->time);
8151   putFile16BitBE(file, level->gems_needed);
8152
8153   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8154     putFile8Bit(file, level->name[i]);
8155
8156   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8157     putFile8Bit(file, level->score[i]);
8158
8159   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8160     for (y = 0; y < 3; y++)
8161       for (x = 0; x < 3; x++)
8162         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8163                            level->yamyam_content[i].e[x][y]));
8164   putFile8Bit(file, level->amoeba_speed);
8165   putFile8Bit(file, level->time_magic_wall);
8166   putFile8Bit(file, level->time_wheel);
8167   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8168                      level->amoeba_content));
8169   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8170   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8171   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8172   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8173
8174   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8175
8176   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8177   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8178   putFile32BitBE(file, level->can_move_into_acid_bits);
8179   putFile8Bit(file, level->dont_collide_with_bits);
8180
8181   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8182   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8183
8184   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8185   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8186   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8187
8188   putFile8Bit(file, level->game_engine_type);
8189
8190   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8191 }
8192 #endif
8193
8194 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8195 {
8196   int chunk_size = 0;
8197   int i;
8198
8199   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8200     chunk_size += putFile8Bit(file, level->name[i]);
8201
8202   return chunk_size;
8203 }
8204
8205 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8206 {
8207   int chunk_size = 0;
8208   int i;
8209
8210   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8211     chunk_size += putFile8Bit(file, level->author[i]);
8212
8213   return chunk_size;
8214 }
8215
8216 #if ENABLE_HISTORIC_CHUNKS
8217 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8218 {
8219   int chunk_size = 0;
8220   int x, y;
8221
8222   for (y = 0; y < level->fieldy; y++)
8223     for (x = 0; x < level->fieldx; x++)
8224       if (level->encoding_16bit_field)
8225         chunk_size += putFile16BitBE(file, level->field[x][y]);
8226       else
8227         chunk_size += putFile8Bit(file, level->field[x][y]);
8228
8229   return chunk_size;
8230 }
8231 #endif
8232
8233 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8234 {
8235   int chunk_size = 0;
8236   int x, y;
8237
8238   for (y = 0; y < level->fieldy; y++) 
8239     for (x = 0; x < level->fieldx; x++) 
8240       chunk_size += putFile16BitBE(file, level->field[x][y]);
8241
8242   return chunk_size;
8243 }
8244
8245 #if ENABLE_HISTORIC_CHUNKS
8246 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8247 {
8248   int i, x, y;
8249
8250   putFile8Bit(file, EL_YAMYAM);
8251   putFile8Bit(file, level->num_yamyam_contents);
8252   putFile8Bit(file, 0);
8253   putFile8Bit(file, 0);
8254
8255   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8256     for (y = 0; y < 3; y++)
8257       for (x = 0; x < 3; x++)
8258         if (level->encoding_16bit_field)
8259           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8260         else
8261           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8262 }
8263 #endif
8264
8265 #if ENABLE_HISTORIC_CHUNKS
8266 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8267 {
8268   int i, x, y;
8269   int num_contents, content_xsize, content_ysize;
8270   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8271
8272   if (element == EL_YAMYAM)
8273   {
8274     num_contents = level->num_yamyam_contents;
8275     content_xsize = 3;
8276     content_ysize = 3;
8277
8278     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8279       for (y = 0; y < 3; y++)
8280         for (x = 0; x < 3; x++)
8281           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8282   }
8283   else if (element == EL_BD_AMOEBA)
8284   {
8285     num_contents = 1;
8286     content_xsize = 1;
8287     content_ysize = 1;
8288
8289     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8290       for (y = 0; y < 3; y++)
8291         for (x = 0; x < 3; x++)
8292           content_array[i][x][y] = EL_EMPTY;
8293     content_array[0][0][0] = level->amoeba_content;
8294   }
8295   else
8296   {
8297     // chunk header already written -- write empty chunk data
8298     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8299
8300     Warn("cannot save content for element '%d'", element);
8301
8302     return;
8303   }
8304
8305   putFile16BitBE(file, element);
8306   putFile8Bit(file, num_contents);
8307   putFile8Bit(file, content_xsize);
8308   putFile8Bit(file, content_ysize);
8309
8310   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8311
8312   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8313     for (y = 0; y < 3; y++)
8314       for (x = 0; x < 3; x++)
8315         putFile16BitBE(file, content_array[i][x][y]);
8316 }
8317 #endif
8318
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8321 {
8322   int envelope_nr = element - EL_ENVELOPE_1;
8323   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8324   int chunk_size = 0;
8325   int i;
8326
8327   chunk_size += putFile16BitBE(file, element);
8328   chunk_size += putFile16BitBE(file, envelope_len);
8329   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8330   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8331
8332   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8333   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8334
8335   for (i = 0; i < envelope_len; i++)
8336     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8337
8338   return chunk_size;
8339 }
8340 #endif
8341
8342 #if ENABLE_HISTORIC_CHUNKS
8343 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8344                            int num_changed_custom_elements)
8345 {
8346   int i, check = 0;
8347
8348   putFile16BitBE(file, num_changed_custom_elements);
8349
8350   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8351   {
8352     int element = EL_CUSTOM_START + i;
8353
8354     struct ElementInfo *ei = &element_info[element];
8355
8356     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8357     {
8358       if (check < num_changed_custom_elements)
8359       {
8360         putFile16BitBE(file, element);
8361         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8362       }
8363
8364       check++;
8365     }
8366   }
8367
8368   if (check != num_changed_custom_elements)     // should not happen
8369     Warn("inconsistent number of custom element properties");
8370 }
8371 #endif
8372
8373 #if ENABLE_HISTORIC_CHUNKS
8374 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8375                            int num_changed_custom_elements)
8376 {
8377   int i, check = 0;
8378
8379   putFile16BitBE(file, num_changed_custom_elements);
8380
8381   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8382   {
8383     int element = EL_CUSTOM_START + i;
8384
8385     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8386     {
8387       if (check < num_changed_custom_elements)
8388       {
8389         putFile16BitBE(file, element);
8390         putFile16BitBE(file, element_info[element].change->target_element);
8391       }
8392
8393       check++;
8394     }
8395   }
8396
8397   if (check != num_changed_custom_elements)     // should not happen
8398     Warn("inconsistent number of custom target elements");
8399 }
8400 #endif
8401
8402 #if ENABLE_HISTORIC_CHUNKS
8403 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8404                            int num_changed_custom_elements)
8405 {
8406   int i, j, x, y, check = 0;
8407
8408   putFile16BitBE(file, num_changed_custom_elements);
8409
8410   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8411   {
8412     int element = EL_CUSTOM_START + i;
8413     struct ElementInfo *ei = &element_info[element];
8414
8415     if (ei->modified_settings)
8416     {
8417       if (check < num_changed_custom_elements)
8418       {
8419         putFile16BitBE(file, element);
8420
8421         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8422           putFile8Bit(file, ei->description[j]);
8423
8424         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8425
8426         // some free bytes for future properties and padding
8427         WriteUnusedBytesToFile(file, 7);
8428
8429         putFile8Bit(file, ei->use_gfx_element);
8430         putFile16BitBE(file, ei->gfx_element_initial);
8431
8432         putFile8Bit(file, ei->collect_score_initial);
8433         putFile8Bit(file, ei->collect_count_initial);
8434
8435         putFile16BitBE(file, ei->push_delay_fixed);
8436         putFile16BitBE(file, ei->push_delay_random);
8437         putFile16BitBE(file, ei->move_delay_fixed);
8438         putFile16BitBE(file, ei->move_delay_random);
8439
8440         putFile16BitBE(file, ei->move_pattern);
8441         putFile8Bit(file, ei->move_direction_initial);
8442         putFile8Bit(file, ei->move_stepsize);
8443
8444         for (y = 0; y < 3; y++)
8445           for (x = 0; x < 3; x++)
8446             putFile16BitBE(file, ei->content.e[x][y]);
8447
8448         putFile32BitBE(file, ei->change->events);
8449
8450         putFile16BitBE(file, ei->change->target_element);
8451
8452         putFile16BitBE(file, ei->change->delay_fixed);
8453         putFile16BitBE(file, ei->change->delay_random);
8454         putFile16BitBE(file, ei->change->delay_frames);
8455
8456         putFile16BitBE(file, ei->change->initial_trigger_element);
8457
8458         putFile8Bit(file, ei->change->explode);
8459         putFile8Bit(file, ei->change->use_target_content);
8460         putFile8Bit(file, ei->change->only_if_complete);
8461         putFile8Bit(file, ei->change->use_random_replace);
8462
8463         putFile8Bit(file, ei->change->random_percentage);
8464         putFile8Bit(file, ei->change->replace_when);
8465
8466         for (y = 0; y < 3; y++)
8467           for (x = 0; x < 3; x++)
8468             putFile16BitBE(file, ei->change->content.e[x][y]);
8469
8470         putFile8Bit(file, ei->slippery_type);
8471
8472         // some free bytes for future properties and padding
8473         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8474       }
8475
8476       check++;
8477     }
8478   }
8479
8480   if (check != num_changed_custom_elements)     // should not happen
8481     Warn("inconsistent number of custom element properties");
8482 }
8483 #endif
8484
8485 #if ENABLE_HISTORIC_CHUNKS
8486 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8487 {
8488   struct ElementInfo *ei = &element_info[element];
8489   int i, j, x, y;
8490
8491   // ---------- custom element base property values (96 bytes) ----------------
8492
8493   putFile16BitBE(file, element);
8494
8495   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8496     putFile8Bit(file, ei->description[i]);
8497
8498   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8499
8500   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8501
8502   putFile8Bit(file, ei->num_change_pages);
8503
8504   putFile16BitBE(file, ei->ce_value_fixed_initial);
8505   putFile16BitBE(file, ei->ce_value_random_initial);
8506   putFile8Bit(file, ei->use_last_ce_value);
8507
8508   putFile8Bit(file, ei->use_gfx_element);
8509   putFile16BitBE(file, ei->gfx_element_initial);
8510
8511   putFile8Bit(file, ei->collect_score_initial);
8512   putFile8Bit(file, ei->collect_count_initial);
8513
8514   putFile8Bit(file, ei->drop_delay_fixed);
8515   putFile8Bit(file, ei->push_delay_fixed);
8516   putFile8Bit(file, ei->drop_delay_random);
8517   putFile8Bit(file, ei->push_delay_random);
8518   putFile16BitBE(file, ei->move_delay_fixed);
8519   putFile16BitBE(file, ei->move_delay_random);
8520
8521   // bits 0 - 15 of "move_pattern" ...
8522   putFile16BitBE(file, ei->move_pattern & 0xffff);
8523   putFile8Bit(file, ei->move_direction_initial);
8524   putFile8Bit(file, ei->move_stepsize);
8525
8526   putFile8Bit(file, ei->slippery_type);
8527
8528   for (y = 0; y < 3; y++)
8529     for (x = 0; x < 3; x++)
8530       putFile16BitBE(file, ei->content.e[x][y]);
8531
8532   putFile16BitBE(file, ei->move_enter_element);
8533   putFile16BitBE(file, ei->move_leave_element);
8534   putFile8Bit(file, ei->move_leave_type);
8535
8536   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8537   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8538
8539   putFile8Bit(file, ei->access_direction);
8540
8541   putFile8Bit(file, ei->explosion_delay);
8542   putFile8Bit(file, ei->ignition_delay);
8543   putFile8Bit(file, ei->explosion_type);
8544
8545   // some free bytes for future custom property values and padding
8546   WriteUnusedBytesToFile(file, 1);
8547
8548   // ---------- change page property values (48 bytes) ------------------------
8549
8550   for (i = 0; i < ei->num_change_pages; i++)
8551   {
8552     struct ElementChangeInfo *change = &ei->change_page[i];
8553     unsigned int event_bits;
8554
8555     // bits 0 - 31 of "has_event[]" ...
8556     event_bits = 0;
8557     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8558       if (change->has_event[j])
8559         event_bits |= (1u << j);
8560     putFile32BitBE(file, event_bits);
8561
8562     putFile16BitBE(file, change->target_element);
8563
8564     putFile16BitBE(file, change->delay_fixed);
8565     putFile16BitBE(file, change->delay_random);
8566     putFile16BitBE(file, change->delay_frames);
8567
8568     putFile16BitBE(file, change->initial_trigger_element);
8569
8570     putFile8Bit(file, change->explode);
8571     putFile8Bit(file, change->use_target_content);
8572     putFile8Bit(file, change->only_if_complete);
8573     putFile8Bit(file, change->use_random_replace);
8574
8575     putFile8Bit(file, change->random_percentage);
8576     putFile8Bit(file, change->replace_when);
8577
8578     for (y = 0; y < 3; y++)
8579       for (x = 0; x < 3; x++)
8580         putFile16BitBE(file, change->target_content.e[x][y]);
8581
8582     putFile8Bit(file, change->can_change);
8583
8584     putFile8Bit(file, change->trigger_side);
8585
8586     putFile8Bit(file, change->trigger_player);
8587     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8588                        log_2(change->trigger_page)));
8589
8590     putFile8Bit(file, change->has_action);
8591     putFile8Bit(file, change->action_type);
8592     putFile8Bit(file, change->action_mode);
8593     putFile16BitBE(file, change->action_arg);
8594
8595     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8596     event_bits = 0;
8597     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8598       if (change->has_event[j])
8599         event_bits |= (1u << (j - 32));
8600     putFile8Bit(file, event_bits);
8601   }
8602 }
8603 #endif
8604
8605 #if ENABLE_HISTORIC_CHUNKS
8606 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8607 {
8608   struct ElementInfo *ei = &element_info[element];
8609   struct ElementGroupInfo *group = ei->group;
8610   int i;
8611
8612   putFile16BitBE(file, element);
8613
8614   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8615     putFile8Bit(file, ei->description[i]);
8616
8617   putFile8Bit(file, group->num_elements);
8618
8619   putFile8Bit(file, ei->use_gfx_element);
8620   putFile16BitBE(file, ei->gfx_element_initial);
8621
8622   putFile8Bit(file, group->choice_mode);
8623
8624   // some free bytes for future values and padding
8625   WriteUnusedBytesToFile(file, 3);
8626
8627   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8628     putFile16BitBE(file, group->element[i]);
8629 }
8630 #endif
8631
8632 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8633                                 boolean write_element)
8634 {
8635   int save_type = entry->save_type;
8636   int data_type = entry->data_type;
8637   int conf_type = entry->conf_type;
8638   int byte_mask = conf_type & CONF_MASK_BYTES;
8639   int element = entry->element;
8640   int default_value = entry->default_value;
8641   int num_bytes = 0;
8642   boolean modified = FALSE;
8643
8644   if (byte_mask != CONF_MASK_MULTI_BYTES)
8645   {
8646     void *value_ptr = entry->value;
8647     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8648                  *(int *)value_ptr);
8649
8650     // check if any settings have been modified before saving them
8651     if (value != default_value)
8652       modified = TRUE;
8653
8654     // do not save if explicitly told or if unmodified default settings
8655     if ((save_type == SAVE_CONF_NEVER) ||
8656         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8657       return 0;
8658
8659     if (write_element)
8660       num_bytes += putFile16BitBE(file, element);
8661
8662     num_bytes += putFile8Bit(file, conf_type);
8663     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8664                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8665                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8666                   0);
8667   }
8668   else if (data_type == TYPE_STRING)
8669   {
8670     char *default_string = entry->default_string;
8671     char *string = (char *)(entry->value);
8672     int string_length = strlen(string);
8673     int i;
8674
8675     // check if any settings have been modified before saving them
8676     if (!strEqual(string, default_string))
8677       modified = TRUE;
8678
8679     // do not save if explicitly told or if unmodified default settings
8680     if ((save_type == SAVE_CONF_NEVER) ||
8681         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8682       return 0;
8683
8684     if (write_element)
8685       num_bytes += putFile16BitBE(file, element);
8686
8687     num_bytes += putFile8Bit(file, conf_type);
8688     num_bytes += putFile16BitBE(file, string_length);
8689
8690     for (i = 0; i < string_length; i++)
8691       num_bytes += putFile8Bit(file, string[i]);
8692   }
8693   else if (data_type == TYPE_ELEMENT_LIST)
8694   {
8695     int *element_array = (int *)(entry->value);
8696     int num_elements = *(int *)(entry->num_entities);
8697     int i;
8698
8699     // check if any settings have been modified before saving them
8700     for (i = 0; i < num_elements; i++)
8701       if (element_array[i] != default_value)
8702         modified = TRUE;
8703
8704     // do not save if explicitly told or if unmodified default settings
8705     if ((save_type == SAVE_CONF_NEVER) ||
8706         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8707       return 0;
8708
8709     if (write_element)
8710       num_bytes += putFile16BitBE(file, element);
8711
8712     num_bytes += putFile8Bit(file, conf_type);
8713     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8714
8715     for (i = 0; i < num_elements; i++)
8716       num_bytes += putFile16BitBE(file, element_array[i]);
8717   }
8718   else if (data_type == TYPE_CONTENT_LIST)
8719   {
8720     struct Content *content = (struct Content *)(entry->value);
8721     int num_contents = *(int *)(entry->num_entities);
8722     int i, x, y;
8723
8724     // check if any settings have been modified before saving them
8725     for (i = 0; i < num_contents; i++)
8726       for (y = 0; y < 3; y++)
8727         for (x = 0; x < 3; x++)
8728           if (content[i].e[x][y] != default_value)
8729             modified = TRUE;
8730
8731     // do not save if explicitly told or if unmodified default settings
8732     if ((save_type == SAVE_CONF_NEVER) ||
8733         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8734       return 0;
8735
8736     if (write_element)
8737       num_bytes += putFile16BitBE(file, element);
8738
8739     num_bytes += putFile8Bit(file, conf_type);
8740     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8741
8742     for (i = 0; i < num_contents; i++)
8743       for (y = 0; y < 3; y++)
8744         for (x = 0; x < 3; x++)
8745           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8746   }
8747
8748   return num_bytes;
8749 }
8750
8751 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8752 {
8753   int chunk_size = 0;
8754   int i;
8755
8756   li = *level;          // copy level data into temporary buffer
8757
8758   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8759     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8760
8761   return chunk_size;
8762 }
8763
8764 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8765 {
8766   int chunk_size = 0;
8767   int i;
8768
8769   li = *level;          // copy level data into temporary buffer
8770
8771   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8772     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8773
8774   return chunk_size;
8775 }
8776
8777 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8778 {
8779   int envelope_nr = element - EL_ENVELOPE_1;
8780   int chunk_size = 0;
8781   int i;
8782
8783   chunk_size += putFile16BitBE(file, element);
8784
8785   // copy envelope data into temporary buffer
8786   xx_envelope = level->envelope[envelope_nr];
8787
8788   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8789     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8790
8791   return chunk_size;
8792 }
8793
8794 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8795 {
8796   struct ElementInfo *ei = &element_info[element];
8797   int chunk_size = 0;
8798   int i, j;
8799
8800   chunk_size += putFile16BitBE(file, element);
8801
8802   xx_ei = *ei;          // copy element data into temporary buffer
8803
8804   // set default description string for this specific element
8805   strcpy(xx_default_description, getDefaultElementDescription(ei));
8806
8807   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8808     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8809
8810   for (i = 0; i < ei->num_change_pages; i++)
8811   {
8812     struct ElementChangeInfo *change = &ei->change_page[i];
8813
8814     xx_current_change_page = i;
8815
8816     xx_change = *change;        // copy change data into temporary buffer
8817
8818     resetEventBits();
8819     setEventBitsFromEventFlags(change);
8820
8821     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8822       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8823                                          FALSE);
8824   }
8825
8826   return chunk_size;
8827 }
8828
8829 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8830 {
8831   struct ElementInfo *ei = &element_info[element];
8832   struct ElementGroupInfo *group = ei->group;
8833   int chunk_size = 0;
8834   int i;
8835
8836   chunk_size += putFile16BitBE(file, element);
8837
8838   xx_ei = *ei;          // copy element data into temporary buffer
8839   xx_group = *group;    // copy group data into temporary buffer
8840
8841   // set default description string for this specific element
8842   strcpy(xx_default_description, getDefaultElementDescription(ei));
8843
8844   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8845     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8846
8847   return chunk_size;
8848 }
8849
8850 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8851 {
8852   struct ElementInfo *ei = &element_info[element];
8853   int chunk_size = 0;
8854   int i;
8855
8856   chunk_size += putFile16BitBE(file, element);
8857
8858   xx_ei = *ei;          // copy element data into temporary buffer
8859
8860   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8861     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8862
8863   return chunk_size;
8864 }
8865
8866 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8867                                   boolean save_as_template)
8868 {
8869   int chunk_size;
8870   int i;
8871   FILE *file;
8872
8873   if (!(file = fopen(filename, MODE_WRITE)))
8874   {
8875     Warn("cannot save level file '%s'", filename);
8876
8877     return;
8878   }
8879
8880   level->file_version = FILE_VERSION_ACTUAL;
8881   level->game_version = GAME_VERSION_ACTUAL;
8882
8883   level->creation_date = getCurrentDate();
8884
8885   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8886   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8887
8888   chunk_size = SaveLevel_VERS(NULL, level);
8889   putFileChunkBE(file, "VERS", chunk_size);
8890   SaveLevel_VERS(file, level);
8891
8892   chunk_size = SaveLevel_DATE(NULL, level);
8893   putFileChunkBE(file, "DATE", chunk_size);
8894   SaveLevel_DATE(file, level);
8895
8896   chunk_size = SaveLevel_NAME(NULL, level);
8897   putFileChunkBE(file, "NAME", chunk_size);
8898   SaveLevel_NAME(file, level);
8899
8900   chunk_size = SaveLevel_AUTH(NULL, level);
8901   putFileChunkBE(file, "AUTH", chunk_size);
8902   SaveLevel_AUTH(file, level);
8903
8904   chunk_size = SaveLevel_INFO(NULL, level);
8905   putFileChunkBE(file, "INFO", chunk_size);
8906   SaveLevel_INFO(file, level);
8907
8908   chunk_size = SaveLevel_BODY(NULL, level);
8909   putFileChunkBE(file, "BODY", chunk_size);
8910   SaveLevel_BODY(file, level);
8911
8912   chunk_size = SaveLevel_ELEM(NULL, level);
8913   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8914   {
8915     putFileChunkBE(file, "ELEM", chunk_size);
8916     SaveLevel_ELEM(file, level);
8917   }
8918
8919   for (i = 0; i < NUM_ENVELOPES; i++)
8920   {
8921     int element = EL_ENVELOPE_1 + i;
8922
8923     chunk_size = SaveLevel_NOTE(NULL, level, element);
8924     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8925     {
8926       putFileChunkBE(file, "NOTE", chunk_size);
8927       SaveLevel_NOTE(file, level, element);
8928     }
8929   }
8930
8931   // if not using template level, check for non-default custom/group elements
8932   if (!level->use_custom_template || save_as_template)
8933   {
8934     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8935     {
8936       int element = EL_CUSTOM_START + i;
8937
8938       chunk_size = SaveLevel_CUSX(NULL, level, element);
8939       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8940       {
8941         putFileChunkBE(file, "CUSX", chunk_size);
8942         SaveLevel_CUSX(file, level, element);
8943       }
8944     }
8945
8946     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8947     {
8948       int element = EL_GROUP_START + i;
8949
8950       chunk_size = SaveLevel_GRPX(NULL, level, element);
8951       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8952       {
8953         putFileChunkBE(file, "GRPX", chunk_size);
8954         SaveLevel_GRPX(file, level, element);
8955       }
8956     }
8957
8958     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8959     {
8960       int element = GET_EMPTY_ELEMENT(i);
8961
8962       chunk_size = SaveLevel_EMPX(NULL, level, element);
8963       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8964       {
8965         putFileChunkBE(file, "EMPX", chunk_size);
8966         SaveLevel_EMPX(file, level, element);
8967       }
8968     }
8969   }
8970
8971   fclose(file);
8972
8973   SetFilePermissions(filename, PERMS_PRIVATE);
8974 }
8975
8976 void SaveLevel(int nr)
8977 {
8978   char *filename = getDefaultLevelFilename(nr);
8979
8980   SaveLevelFromFilename(&level, filename, FALSE);
8981 }
8982
8983 void SaveLevelTemplate(void)
8984 {
8985   char *filename = getLocalLevelTemplateFilename();
8986
8987   SaveLevelFromFilename(&level, filename, TRUE);
8988 }
8989
8990 boolean SaveLevelChecked(int nr)
8991 {
8992   char *filename = getDefaultLevelFilename(nr);
8993   boolean new_level = !fileExists(filename);
8994   boolean level_saved = FALSE;
8995
8996   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8997   {
8998     SaveLevel(nr);
8999
9000     if (new_level)
9001       Request("Level saved!", REQ_CONFIRM);
9002
9003     level_saved = TRUE;
9004   }
9005
9006   return level_saved;
9007 }
9008
9009 void DumpLevel(struct LevelInfo *level)
9010 {
9011   if (level->no_level_file || level->no_valid_file)
9012   {
9013     Warn("cannot dump -- no valid level file found");
9014
9015     return;
9016   }
9017
9018   PrintLine("-", 79);
9019   Print("Level xxx (file version %08d, game version %08d)\n",
9020         level->file_version, level->game_version);
9021   PrintLine("-", 79);
9022
9023   Print("Level author: '%s'\n", level->author);
9024   Print("Level title:  '%s'\n", level->name);
9025   Print("\n");
9026   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
9027   Print("\n");
9028   Print("Level time:  %d seconds\n", level->time);
9029   Print("Gems needed: %d\n", level->gems_needed);
9030   Print("\n");
9031   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
9032   Print("Time for wheel:      %d seconds\n", level->time_wheel);
9033   Print("Time for light:      %d seconds\n", level->time_light);
9034   Print("Time for timegate:   %d seconds\n", level->time_timegate);
9035   Print("\n");
9036   Print("Amoeba speed: %d\n", level->amoeba_speed);
9037   Print("\n");
9038
9039   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
9040   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
9041   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
9042   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
9043   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
9044   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
9045
9046   if (options.debug)
9047   {
9048     int i, j;
9049
9050     for (i = 0; i < NUM_ENVELOPES; i++)
9051     {
9052       char *text = level->envelope[i].text;
9053       int text_len = strlen(text);
9054       boolean has_text = FALSE;
9055
9056       for (j = 0; j < text_len; j++)
9057         if (text[j] != ' ' && text[j] != '\n')
9058           has_text = TRUE;
9059
9060       if (has_text)
9061       {
9062         Print("\n");
9063         Print("Envelope %d:\n'%s'\n", i + 1, text);
9064       }
9065     }
9066   }
9067
9068   PrintLine("-", 79);
9069 }
9070
9071 void DumpLevels(void)
9072 {
9073   static LevelDirTree *dumplevel_leveldir = NULL;
9074
9075   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9076                                                  global.dumplevel_leveldir);
9077
9078   if (dumplevel_leveldir == NULL)
9079     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
9080
9081   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
9082       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9083     Fail("no such level number: %d", global.dumplevel_level_nr);
9084
9085   leveldir_current = dumplevel_leveldir;
9086
9087   LoadLevel(global.dumplevel_level_nr);
9088   DumpLevel(&level);
9089
9090   CloseAllAndExit(0);
9091 }
9092
9093 void DumpLevelsetFromFilename_BD(char *filename)
9094 {
9095   if (leveldir_current == NULL) // no levelsets loaded yet
9096     bd_open_all();
9097
9098   if (!LoadNativeLevel_BD(filename, 0, FALSE))
9099     CloseAllAndExit(0);         // function has already printed warning
9100
9101   PrintLine("-", 79);
9102   Print("Levelset '%s'\n", filename);
9103   PrintLine("-", 79);
9104
9105   DumpLevelset_BD();
9106
9107   PrintLine("-", 79);
9108
9109   CloseAllAndExit(0);
9110 }
9111
9112 void DumpLevelset(void)
9113 {
9114   static LevelDirTree *dumplevelset_leveldir = NULL;
9115
9116   dumplevelset_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9117                                                     global.dumplevelset_leveldir);
9118   if (dumplevelset_leveldir == NULL)
9119     Fail("no such level identifier: '%s'", global.dumplevelset_leveldir);
9120
9121   PrintLine("-", 79);
9122   Print("Levelset '%s'\n", dumplevelset_leveldir->identifier);
9123   PrintLine("-", 79);
9124
9125   Print("Number of levels:   %d\n", dumplevelset_leveldir->levels);
9126   Print("First level number: %d\n", dumplevelset_leveldir->first_level);
9127
9128   PrintLine("-", 79);
9129
9130   CloseAllAndExit(0);
9131 }
9132
9133
9134 // ============================================================================
9135 // tape file functions
9136 // ============================================================================
9137
9138 static void setTapeInfoToDefaults(void)
9139 {
9140   int i;
9141
9142   // always start with reliable default values (empty tape)
9143   TapeErase();
9144
9145   // default values (also for pre-1.2 tapes) with only the first player
9146   tape.player_participates[0] = TRUE;
9147   for (i = 1; i < MAX_PLAYERS; i++)
9148     tape.player_participates[i] = FALSE;
9149
9150   // at least one (default: the first) player participates in every tape
9151   tape.num_participating_players = 1;
9152
9153   tape.property_bits = TAPE_PROPERTY_NONE;
9154
9155   tape.level_nr = level_nr;
9156   tape.counter = 0;
9157   tape.changed = FALSE;
9158   tape.solved = FALSE;
9159
9160   tape.recording = FALSE;
9161   tape.playing = FALSE;
9162   tape.pausing = FALSE;
9163
9164   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9165   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9166
9167   tape.no_info_chunk = TRUE;
9168   tape.no_valid_file = FALSE;
9169 }
9170
9171 static int getTapePosSize(struct TapeInfo *tape)
9172 {
9173   int tape_pos_size = 0;
9174
9175   if (tape->use_key_actions)
9176     tape_pos_size += tape->num_participating_players;
9177
9178   if (tape->use_mouse_actions)
9179     tape_pos_size += 3;         // x and y position and mouse button mask
9180
9181   tape_pos_size += 1;           // tape action delay value
9182
9183   return tape_pos_size;
9184 }
9185
9186 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9187 {
9188   tape->use_key_actions = FALSE;
9189   tape->use_mouse_actions = FALSE;
9190
9191   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9192     tape->use_key_actions = TRUE;
9193
9194   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9195     tape->use_mouse_actions = TRUE;
9196 }
9197
9198 static int getTapeActionValue(struct TapeInfo *tape)
9199 {
9200   return (tape->use_key_actions &&
9201           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9202           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9203           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9204           TAPE_ACTIONS_DEFAULT);
9205 }
9206
9207 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9208 {
9209   tape->file_version = getFileVersion(file);
9210   tape->game_version = getFileVersion(file);
9211
9212   return chunk_size;
9213 }
9214
9215 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9216 {
9217   int i;
9218
9219   tape->random_seed = getFile32BitBE(file);
9220   tape->date        = getFile32BitBE(file);
9221   tape->length      = getFile32BitBE(file);
9222
9223   // read header fields that are new since version 1.2
9224   if (tape->file_version >= FILE_VERSION_1_2)
9225   {
9226     byte store_participating_players = getFile8Bit(file);
9227     int engine_version;
9228
9229     // since version 1.2, tapes store which players participate in the tape
9230     tape->num_participating_players = 0;
9231     for (i = 0; i < MAX_PLAYERS; i++)
9232     {
9233       tape->player_participates[i] = FALSE;
9234
9235       if (store_participating_players & (1 << i))
9236       {
9237         tape->player_participates[i] = TRUE;
9238         tape->num_participating_players++;
9239       }
9240     }
9241
9242     setTapeActionFlags(tape, getFile8Bit(file));
9243
9244     tape->property_bits = getFile8Bit(file);
9245     tape->solved = getFile8Bit(file);
9246
9247     engine_version = getFileVersion(file);
9248     if (engine_version > 0)
9249       tape->engine_version = engine_version;
9250     else
9251       tape->engine_version = tape->game_version;
9252   }
9253
9254   return chunk_size;
9255 }
9256
9257 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9258 {
9259   tape->scr_fieldx = getFile8Bit(file);
9260   tape->scr_fieldy = getFile8Bit(file);
9261
9262   return chunk_size;
9263 }
9264
9265 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9266 {
9267   char *level_identifier = NULL;
9268   int level_identifier_size;
9269   int i;
9270
9271   tape->no_info_chunk = FALSE;
9272
9273   level_identifier_size = getFile16BitBE(file);
9274
9275   level_identifier = checked_malloc(level_identifier_size);
9276
9277   for (i = 0; i < level_identifier_size; i++)
9278     level_identifier[i] = getFile8Bit(file);
9279
9280   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9281   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9282
9283   checked_free(level_identifier);
9284
9285   tape->level_nr = getFile16BitBE(file);
9286
9287   chunk_size = 2 + level_identifier_size + 2;
9288
9289   return chunk_size;
9290 }
9291
9292 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9293 {
9294   int i, j;
9295   int tape_pos_size = getTapePosSize(tape);
9296   int chunk_size_expected = tape_pos_size * tape->length;
9297
9298   if (chunk_size_expected != chunk_size)
9299   {
9300     ReadUnusedBytesFromFile(file, chunk_size);
9301     return chunk_size_expected;
9302   }
9303
9304   for (i = 0; i < tape->length; i++)
9305   {
9306     if (i >= MAX_TAPE_LEN)
9307     {
9308       Warn("tape truncated -- size exceeds maximum tape size %d",
9309             MAX_TAPE_LEN);
9310
9311       // tape too large; read and ignore remaining tape data from this chunk
9312       for (;i < tape->length; i++)
9313         ReadUnusedBytesFromFile(file, tape_pos_size);
9314
9315       break;
9316     }
9317
9318     if (tape->use_key_actions)
9319     {
9320       for (j = 0; j < MAX_PLAYERS; j++)
9321       {
9322         tape->pos[i].action[j] = MV_NONE;
9323
9324         if (tape->player_participates[j])
9325           tape->pos[i].action[j] = getFile8Bit(file);
9326       }
9327     }
9328
9329     if (tape->use_mouse_actions)
9330     {
9331       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9332       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9333       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9334     }
9335
9336     tape->pos[i].delay = getFile8Bit(file);
9337
9338     if (tape->file_version == FILE_VERSION_1_0)
9339     {
9340       // eliminate possible diagonal moves in old tapes
9341       // this is only for backward compatibility
9342
9343       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9344       byte action = tape->pos[i].action[0];
9345       int k, num_moves = 0;
9346
9347       for (k = 0; k < 4; k++)
9348       {
9349         if (action & joy_dir[k])
9350         {
9351           tape->pos[i + num_moves].action[0] = joy_dir[k];
9352           if (num_moves > 0)
9353             tape->pos[i + num_moves].delay = 0;
9354           num_moves++;
9355         }
9356       }
9357
9358       if (num_moves > 1)
9359       {
9360         num_moves--;
9361         i += num_moves;
9362         tape->length += num_moves;
9363       }
9364     }
9365     else if (tape->file_version < FILE_VERSION_2_0)
9366     {
9367       // convert pre-2.0 tapes to new tape format
9368
9369       if (tape->pos[i].delay > 1)
9370       {
9371         // action part
9372         tape->pos[i + 1] = tape->pos[i];
9373         tape->pos[i + 1].delay = 1;
9374
9375         // delay part
9376         for (j = 0; j < MAX_PLAYERS; j++)
9377           tape->pos[i].action[j] = MV_NONE;
9378         tape->pos[i].delay--;
9379
9380         i++;
9381         tape->length++;
9382       }
9383     }
9384
9385     if (checkEndOfFile(file))
9386       break;
9387   }
9388
9389   if (i != tape->length)
9390     chunk_size = tape_pos_size * i;
9391
9392   return chunk_size;
9393 }
9394
9395 static void LoadTape_SokobanSolution(char *filename)
9396 {
9397   File *file;
9398   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9399
9400   if (!(file = openFile(filename, MODE_READ)))
9401   {
9402     tape.no_valid_file = TRUE;
9403
9404     return;
9405   }
9406
9407   while (!checkEndOfFile(file))
9408   {
9409     unsigned char c = getByteFromFile(file);
9410
9411     if (checkEndOfFile(file))
9412       break;
9413
9414     switch (c)
9415     {
9416       case 'u':
9417       case 'U':
9418         tape.pos[tape.length].action[0] = MV_UP;
9419         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9420         tape.length++;
9421         break;
9422
9423       case 'd':
9424       case 'D':
9425         tape.pos[tape.length].action[0] = MV_DOWN;
9426         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9427         tape.length++;
9428         break;
9429
9430       case 'l':
9431       case 'L':
9432         tape.pos[tape.length].action[0] = MV_LEFT;
9433         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9434         tape.length++;
9435         break;
9436
9437       case 'r':
9438       case 'R':
9439         tape.pos[tape.length].action[0] = MV_RIGHT;
9440         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9441         tape.length++;
9442         break;
9443
9444       case '\n':
9445       case '\r':
9446       case '\t':
9447       case ' ':
9448         // ignore white-space characters
9449         break;
9450
9451       default:
9452         tape.no_valid_file = TRUE;
9453
9454         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9455
9456         break;
9457     }
9458   }
9459
9460   closeFile(file);
9461
9462   if (tape.no_valid_file)
9463     return;
9464
9465   tape.length_frames  = GetTapeLengthFrames();
9466   tape.length_seconds = GetTapeLengthSeconds();
9467 }
9468
9469 void LoadTapeFromFilename(char *filename)
9470 {
9471   char cookie[MAX_LINE_LEN];
9472   char chunk_name[CHUNK_ID_LEN + 1];
9473   File *file;
9474   int chunk_size;
9475
9476   // always start with reliable default values
9477   setTapeInfoToDefaults();
9478
9479   if (strSuffix(filename, ".sln"))
9480   {
9481     LoadTape_SokobanSolution(filename);
9482
9483     return;
9484   }
9485
9486   if (!(file = openFile(filename, MODE_READ)))
9487   {
9488     tape.no_valid_file = TRUE;
9489
9490     return;
9491   }
9492
9493   getFileChunkBE(file, chunk_name, NULL);
9494   if (strEqual(chunk_name, "RND1"))
9495   {
9496     getFile32BitBE(file);               // not used
9497
9498     getFileChunkBE(file, chunk_name, NULL);
9499     if (!strEqual(chunk_name, "TAPE"))
9500     {
9501       tape.no_valid_file = TRUE;
9502
9503       Warn("unknown format of tape file '%s'", filename);
9504
9505       closeFile(file);
9506
9507       return;
9508     }
9509   }
9510   else  // check for pre-2.0 file format with cookie string
9511   {
9512     strcpy(cookie, chunk_name);
9513     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9514       cookie[4] = '\0';
9515     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9516       cookie[strlen(cookie) - 1] = '\0';
9517
9518     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9519     {
9520       tape.no_valid_file = TRUE;
9521
9522       Warn("unknown format of tape file '%s'", filename);
9523
9524       closeFile(file);
9525
9526       return;
9527     }
9528
9529     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9530     {
9531       tape.no_valid_file = TRUE;
9532
9533       Warn("unsupported version of tape file '%s'", filename);
9534
9535       closeFile(file);
9536
9537       return;
9538     }
9539
9540     // pre-2.0 tape files have no game version, so use file version here
9541     tape.game_version = tape.file_version;
9542   }
9543
9544   if (tape.file_version < FILE_VERSION_1_2)
9545   {
9546     // tape files from versions before 1.2.0 without chunk structure
9547     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9548     LoadTape_BODY(file, 2 * tape.length,      &tape);
9549   }
9550   else
9551   {
9552     static struct
9553     {
9554       char *name;
9555       int size;
9556       int (*loader)(File *, int, struct TapeInfo *);
9557     }
9558     chunk_info[] =
9559     {
9560       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9561       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9562       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9563       { "INFO", -1,                     LoadTape_INFO },
9564       { "BODY", -1,                     LoadTape_BODY },
9565       {  NULL,  0,                      NULL }
9566     };
9567
9568     while (getFileChunkBE(file, chunk_name, &chunk_size))
9569     {
9570       int i = 0;
9571
9572       while (chunk_info[i].name != NULL &&
9573              !strEqual(chunk_name, chunk_info[i].name))
9574         i++;
9575
9576       if (chunk_info[i].name == NULL)
9577       {
9578         Warn("unknown chunk '%s' in tape file '%s'",
9579               chunk_name, filename);
9580
9581         ReadUnusedBytesFromFile(file, chunk_size);
9582       }
9583       else if (chunk_info[i].size != -1 &&
9584                chunk_info[i].size != chunk_size)
9585       {
9586         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9587               chunk_size, chunk_name, filename);
9588
9589         ReadUnusedBytesFromFile(file, chunk_size);
9590       }
9591       else
9592       {
9593         // call function to load this tape chunk
9594         int chunk_size_expected =
9595           (chunk_info[i].loader)(file, chunk_size, &tape);
9596
9597         // the size of some chunks cannot be checked before reading other
9598         // chunks first (like "HEAD" and "BODY") that contain some header
9599         // information, so check them here
9600         if (chunk_size_expected != chunk_size)
9601         {
9602           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9603                 chunk_size, chunk_name, filename);
9604         }
9605       }
9606     }
9607   }
9608
9609   closeFile(file);
9610
9611   tape.length_frames  = GetTapeLengthFrames();
9612   tape.length_seconds = GetTapeLengthSeconds();
9613
9614 #if 0
9615   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9616         tape.file_version);
9617   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9618         tape.game_version);
9619   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9620         tape.engine_version);
9621 #endif
9622 }
9623
9624 void LoadTape(int nr)
9625 {
9626   char *filename = getTapeFilename(nr);
9627
9628   LoadTapeFromFilename(filename);
9629 }
9630
9631 void LoadSolutionTape(int nr)
9632 {
9633   char *filename = getSolutionTapeFilename(nr);
9634
9635   LoadTapeFromFilename(filename);
9636
9637   if (TAPE_IS_EMPTY(tape))
9638   {
9639     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9640         level.native_bd_level->replay != NULL)
9641       CopyNativeTape_BD_to_RND(&level);
9642     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9643         level.native_sp_level->demo.is_available)
9644       CopyNativeTape_SP_to_RND(&level);
9645   }
9646 }
9647
9648 void LoadScoreTape(char *score_tape_basename, int nr)
9649 {
9650   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9651
9652   LoadTapeFromFilename(filename);
9653 }
9654
9655 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9656 {
9657   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9658
9659   LoadTapeFromFilename(filename);
9660 }
9661
9662 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9663 {
9664   // chunk required for team mode tapes with non-default screen size
9665   return (tape->num_participating_players > 1 &&
9666           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9667            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9668 }
9669
9670 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9671 {
9672   putFileVersion(file, tape->file_version);
9673   putFileVersion(file, tape->game_version);
9674 }
9675
9676 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9677 {
9678   int i;
9679   byte store_participating_players = 0;
9680
9681   // set bits for participating players for compact storage
9682   for (i = 0; i < MAX_PLAYERS; i++)
9683     if (tape->player_participates[i])
9684       store_participating_players |= (1 << i);
9685
9686   putFile32BitBE(file, tape->random_seed);
9687   putFile32BitBE(file, tape->date);
9688   putFile32BitBE(file, tape->length);
9689
9690   putFile8Bit(file, store_participating_players);
9691
9692   putFile8Bit(file, getTapeActionValue(tape));
9693
9694   putFile8Bit(file, tape->property_bits);
9695   putFile8Bit(file, tape->solved);
9696
9697   putFileVersion(file, tape->engine_version);
9698 }
9699
9700 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9701 {
9702   putFile8Bit(file, tape->scr_fieldx);
9703   putFile8Bit(file, tape->scr_fieldy);
9704 }
9705
9706 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9707 {
9708   int level_identifier_size = strlen(tape->level_identifier) + 1;
9709   int i;
9710
9711   putFile16BitBE(file, level_identifier_size);
9712
9713   for (i = 0; i < level_identifier_size; i++)
9714     putFile8Bit(file, tape->level_identifier[i]);
9715
9716   putFile16BitBE(file, tape->level_nr);
9717 }
9718
9719 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9720 {
9721   int i, j;
9722
9723   for (i = 0; i < tape->length; i++)
9724   {
9725     if (tape->use_key_actions)
9726     {
9727       for (j = 0; j < MAX_PLAYERS; j++)
9728         if (tape->player_participates[j])
9729           putFile8Bit(file, tape->pos[i].action[j]);
9730     }
9731
9732     if (tape->use_mouse_actions)
9733     {
9734       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9735       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9736       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9737     }
9738
9739     putFile8Bit(file, tape->pos[i].delay);
9740   }
9741 }
9742
9743 void SaveTapeToFilename(char *filename)
9744 {
9745   FILE *file;
9746   int tape_pos_size;
9747   int info_chunk_size;
9748   int body_chunk_size;
9749
9750   if (!(file = fopen(filename, MODE_WRITE)))
9751   {
9752     Warn("cannot save level recording file '%s'", filename);
9753
9754     return;
9755   }
9756
9757   tape_pos_size = getTapePosSize(&tape);
9758
9759   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9760   body_chunk_size = tape_pos_size * tape.length;
9761
9762   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9763   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9764
9765   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9766   SaveTape_VERS(file, &tape);
9767
9768   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9769   SaveTape_HEAD(file, &tape);
9770
9771   if (checkSaveTape_SCRN(&tape))
9772   {
9773     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9774     SaveTape_SCRN(file, &tape);
9775   }
9776
9777   putFileChunkBE(file, "INFO", info_chunk_size);
9778   SaveTape_INFO(file, &tape);
9779
9780   putFileChunkBE(file, "BODY", body_chunk_size);
9781   SaveTape_BODY(file, &tape);
9782
9783   fclose(file);
9784
9785   SetFilePermissions(filename, PERMS_PRIVATE);
9786 }
9787
9788 static void SaveTapeExt(char *filename)
9789 {
9790   int i;
9791
9792   tape.file_version = FILE_VERSION_ACTUAL;
9793   tape.game_version = GAME_VERSION_ACTUAL;
9794
9795   tape.num_participating_players = 0;
9796
9797   // count number of participating players
9798   for (i = 0; i < MAX_PLAYERS; i++)
9799     if (tape.player_participates[i])
9800       tape.num_participating_players++;
9801
9802   SaveTapeToFilename(filename);
9803
9804   tape.changed = FALSE;
9805 }
9806
9807 void SaveTape(int nr)
9808 {
9809   char *filename = getTapeFilename(nr);
9810
9811   InitTapeDirectory(leveldir_current->subdir);
9812
9813   SaveTapeExt(filename);
9814 }
9815
9816 void SaveScoreTape(int nr)
9817 {
9818   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9819
9820   // used instead of "leveldir_current->subdir" (for network games)
9821   InitScoreTapeDirectory(levelset.identifier, nr);
9822
9823   SaveTapeExt(filename);
9824 }
9825
9826 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9827                                   unsigned int req_state_added)
9828 {
9829   char *filename = getTapeFilename(nr);
9830   boolean new_tape = !fileExists(filename);
9831   boolean tape_saved = FALSE;
9832
9833   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9834   {
9835     SaveTape(nr);
9836
9837     if (new_tape)
9838       Request(msg_saved, REQ_CONFIRM | req_state_added);
9839
9840     tape_saved = TRUE;
9841   }
9842
9843   return tape_saved;
9844 }
9845
9846 boolean SaveTapeChecked(int nr)
9847 {
9848   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9849 }
9850
9851 boolean SaveTapeChecked_LevelSolved(int nr)
9852 {
9853   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9854                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9855 }
9856
9857 void DumpTape(struct TapeInfo *tape)
9858 {
9859   int tape_frame_counter;
9860   int i, j;
9861
9862   if (tape->no_valid_file)
9863   {
9864     Warn("cannot dump -- no valid tape file found");
9865
9866     return;
9867   }
9868
9869   PrintLine("-", 79);
9870
9871   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9872         tape->level_nr, tape->file_version, tape->game_version);
9873   Print("                  (effective engine version %08d)\n",
9874         tape->engine_version);
9875   Print("Level series identifier: '%s'\n", tape->level_identifier);
9876
9877   Print("Solution tape: %s\n",
9878         tape->solved ? "yes" :
9879         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9880
9881   Print("Special tape properties: ");
9882   if (tape->property_bits == TAPE_PROPERTY_NONE)
9883     Print("[none]");
9884   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9885     Print("[em_random_bug]");
9886   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9887     Print("[game_speed]");
9888   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9889     Print("[pause]");
9890   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9891     Print("[single_step]");
9892   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9893     Print("[snapshot]");
9894   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9895     Print("[replayed]");
9896   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9897     Print("[tas_keys]");
9898   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9899     Print("[small_graphics]");
9900   Print("\n");
9901
9902   int year2 = tape->date / 10000;
9903   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9904   int month_index_raw = (tape->date / 100) % 100;
9905   int month_index = month_index_raw % 12;       // prevent invalid index
9906   int month = month_index + 1;
9907   int day = tape->date % 100;
9908
9909   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9910
9911   PrintLine("-", 79);
9912
9913   tape_frame_counter = 0;
9914
9915   for (i = 0; i < tape->length; i++)
9916   {
9917     if (i >= MAX_TAPE_LEN)
9918       break;
9919
9920     Print("%04d: ", i);
9921
9922     for (j = 0; j < MAX_PLAYERS; j++)
9923     {
9924       if (tape->player_participates[j])
9925       {
9926         int action = tape->pos[i].action[j];
9927
9928         Print("%d:%02x ", j, action);
9929         Print("[%c%c%c%c|%c%c] - ",
9930               (action & JOY_LEFT ? '<' : ' '),
9931               (action & JOY_RIGHT ? '>' : ' '),
9932               (action & JOY_UP ? '^' : ' '),
9933               (action & JOY_DOWN ? 'v' : ' '),
9934               (action & JOY_BUTTON_1 ? '1' : ' '),
9935               (action & JOY_BUTTON_2 ? '2' : ' '));
9936       }
9937     }
9938
9939     Print("(%03d) ", tape->pos[i].delay);
9940     Print("[%05d]\n", tape_frame_counter);
9941
9942     tape_frame_counter += tape->pos[i].delay;
9943   }
9944
9945   PrintLine("-", 79);
9946 }
9947
9948 void DumpTapes(void)
9949 {
9950   static LevelDirTree *dumptape_leveldir = NULL;
9951
9952   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9953                                                 global.dumptape_leveldir);
9954
9955   if (dumptape_leveldir == NULL)
9956     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9957
9958   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9959       global.dumptape_level_nr > dumptape_leveldir->last_level)
9960     Fail("no such level number: %d", global.dumptape_level_nr);
9961
9962   leveldir_current = dumptape_leveldir;
9963
9964   if (options.mytapes)
9965     LoadTape(global.dumptape_level_nr);
9966   else
9967     LoadSolutionTape(global.dumptape_level_nr);
9968
9969   DumpTape(&tape);
9970
9971   CloseAllAndExit(0);
9972 }
9973
9974
9975 // ============================================================================
9976 // score file functions
9977 // ============================================================================
9978
9979 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9980 {
9981   int i;
9982
9983   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9984   {
9985     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9986     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9987     scores->entry[i].score = 0;
9988     scores->entry[i].time = 0;
9989
9990     scores->entry[i].id = -1;
9991     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9992     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9993     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9994     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9995     strcpy(scores->entry[i].country_code, "??");
9996   }
9997
9998   scores->num_entries = 0;
9999   scores->last_added = -1;
10000   scores->last_added_local = -1;
10001
10002   scores->updated = FALSE;
10003   scores->uploaded = FALSE;
10004   scores->tape_downloaded = FALSE;
10005   scores->force_last_added = FALSE;
10006
10007   // The following values are intentionally not reset here:
10008   // - last_level_nr
10009   // - last_entry_nr
10010   // - next_level_nr
10011   // - continue_playing
10012   // - continue_on_return
10013 }
10014
10015 static void setScoreInfoToDefaults(void)
10016 {
10017   setScoreInfoToDefaultsExt(&scores);
10018 }
10019
10020 static void setServerScoreInfoToDefaults(void)
10021 {
10022   setScoreInfoToDefaultsExt(&server_scores);
10023 }
10024
10025 static void LoadScore_OLD(int nr)
10026 {
10027   int i;
10028   char *filename = getScoreFilename(nr);
10029   char cookie[MAX_LINE_LEN];
10030   char line[MAX_LINE_LEN];
10031   char *line_ptr;
10032   FILE *file;
10033
10034   if (!(file = fopen(filename, MODE_READ)))
10035     return;
10036
10037   // check file identifier
10038   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
10039     cookie[0] = '\0';
10040   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10041     cookie[strlen(cookie) - 1] = '\0';
10042
10043   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10044   {
10045     Warn("unknown format of score file '%s'", filename);
10046
10047     fclose(file);
10048
10049     return;
10050   }
10051
10052   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10053   {
10054     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
10055       Warn("fscanf() failed; %s", strerror(errno));
10056
10057     if (fgets(line, MAX_LINE_LEN, file) == NULL)
10058       line[0] = '\0';
10059
10060     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
10061       line[strlen(line) - 1] = '\0';
10062
10063     for (line_ptr = line; *line_ptr; line_ptr++)
10064     {
10065       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
10066       {
10067         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
10068         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10069         break;
10070       }
10071     }
10072   }
10073
10074   fclose(file);
10075 }
10076
10077 static void ConvertScore_OLD(void)
10078 {
10079   // only convert score to time for levels that rate playing time over score
10080   if (!level.rate_time_over_score)
10081     return;
10082
10083   // convert old score to playing time for score-less levels (like Supaplex)
10084   int time_final_max = 999;
10085   int i;
10086
10087   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10088   {
10089     int score = scores.entry[i].score;
10090
10091     if (score > 0 && score < time_final_max)
10092       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
10093   }
10094 }
10095
10096 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
10097 {
10098   scores->file_version = getFileVersion(file);
10099   scores->game_version = getFileVersion(file);
10100
10101   return chunk_size;
10102 }
10103
10104 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
10105 {
10106   char *level_identifier = NULL;
10107   int level_identifier_size;
10108   int i;
10109
10110   level_identifier_size = getFile16BitBE(file);
10111
10112   level_identifier = checked_malloc(level_identifier_size);
10113
10114   for (i = 0; i < level_identifier_size; i++)
10115     level_identifier[i] = getFile8Bit(file);
10116
10117   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
10118   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
10119
10120   checked_free(level_identifier);
10121
10122   scores->level_nr = getFile16BitBE(file);
10123   scores->num_entries = getFile16BitBE(file);
10124
10125   chunk_size = 2 + level_identifier_size + 2 + 2;
10126
10127   return chunk_size;
10128 }
10129
10130 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10131 {
10132   int i, j;
10133
10134   for (i = 0; i < scores->num_entries; i++)
10135   {
10136     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10137       scores->entry[i].name[j] = getFile8Bit(file);
10138
10139     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10140   }
10141
10142   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10143
10144   return chunk_size;
10145 }
10146
10147 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10148 {
10149   int i;
10150
10151   for (i = 0; i < scores->num_entries; i++)
10152     scores->entry[i].score = getFile16BitBE(file);
10153
10154   chunk_size = scores->num_entries * 2;
10155
10156   return chunk_size;
10157 }
10158
10159 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10160 {
10161   int i;
10162
10163   for (i = 0; i < scores->num_entries; i++)
10164     scores->entry[i].score = getFile32BitBE(file);
10165
10166   chunk_size = scores->num_entries * 4;
10167
10168   return chunk_size;
10169 }
10170
10171 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10172 {
10173   int i;
10174
10175   for (i = 0; i < scores->num_entries; i++)
10176     scores->entry[i].time = getFile32BitBE(file);
10177
10178   chunk_size = scores->num_entries * 4;
10179
10180   return chunk_size;
10181 }
10182
10183 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10184 {
10185   int i, j;
10186
10187   for (i = 0; i < scores->num_entries; i++)
10188   {
10189     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10190       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10191
10192     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10193   }
10194
10195   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10196
10197   return chunk_size;
10198 }
10199
10200 void LoadScore(int nr)
10201 {
10202   char *filename = getScoreFilename(nr);
10203   char cookie[MAX_LINE_LEN];
10204   char chunk_name[CHUNK_ID_LEN + 1];
10205   int chunk_size;
10206   boolean old_score_file_format = FALSE;
10207   File *file;
10208
10209   // always start with reliable default values
10210   setScoreInfoToDefaults();
10211
10212   if (!(file = openFile(filename, MODE_READ)))
10213     return;
10214
10215   getFileChunkBE(file, chunk_name, NULL);
10216   if (strEqual(chunk_name, "RND1"))
10217   {
10218     getFile32BitBE(file);               // not used
10219
10220     getFileChunkBE(file, chunk_name, NULL);
10221     if (!strEqual(chunk_name, "SCOR"))
10222     {
10223       Warn("unknown format of score file '%s'", filename);
10224
10225       closeFile(file);
10226
10227       return;
10228     }
10229   }
10230   else  // check for old file format with cookie string
10231   {
10232     strcpy(cookie, chunk_name);
10233     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10234       cookie[4] = '\0';
10235     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10236       cookie[strlen(cookie) - 1] = '\0';
10237
10238     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10239     {
10240       Warn("unknown format of score file '%s'", filename);
10241
10242       closeFile(file);
10243
10244       return;
10245     }
10246
10247     old_score_file_format = TRUE;
10248   }
10249
10250   if (old_score_file_format)
10251   {
10252     // score files from versions before 4.2.4.0 without chunk structure
10253     LoadScore_OLD(nr);
10254
10255     // convert score to time, if possible (mainly for Supaplex levels)
10256     ConvertScore_OLD();
10257   }
10258   else
10259   {
10260     static struct
10261     {
10262       char *name;
10263       int size;
10264       int (*loader)(File *, int, struct ScoreInfo *);
10265     }
10266     chunk_info[] =
10267     {
10268       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10269       { "INFO", -1,                     LoadScore_INFO },
10270       { "NAME", -1,                     LoadScore_NAME },
10271       { "SCOR", -1,                     LoadScore_SCOR },
10272       { "SC4R", -1,                     LoadScore_SC4R },
10273       { "TIME", -1,                     LoadScore_TIME },
10274       { "TAPE", -1,                     LoadScore_TAPE },
10275
10276       {  NULL,  0,                      NULL }
10277     };
10278
10279     while (getFileChunkBE(file, chunk_name, &chunk_size))
10280     {
10281       int i = 0;
10282
10283       while (chunk_info[i].name != NULL &&
10284              !strEqual(chunk_name, chunk_info[i].name))
10285         i++;
10286
10287       if (chunk_info[i].name == NULL)
10288       {
10289         Warn("unknown chunk '%s' in score file '%s'",
10290               chunk_name, filename);
10291
10292         ReadUnusedBytesFromFile(file, chunk_size);
10293       }
10294       else if (chunk_info[i].size != -1 &&
10295                chunk_info[i].size != chunk_size)
10296       {
10297         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10298               chunk_size, chunk_name, filename);
10299
10300         ReadUnusedBytesFromFile(file, chunk_size);
10301       }
10302       else
10303       {
10304         // call function to load this score chunk
10305         int chunk_size_expected =
10306           (chunk_info[i].loader)(file, chunk_size, &scores);
10307
10308         // the size of some chunks cannot be checked before reading other
10309         // chunks first (like "HEAD" and "BODY") that contain some header
10310         // information, so check them here
10311         if (chunk_size_expected != chunk_size)
10312         {
10313           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10314                 chunk_size, chunk_name, filename);
10315         }
10316       }
10317     }
10318   }
10319
10320   closeFile(file);
10321 }
10322
10323 #if ENABLE_HISTORIC_CHUNKS
10324 void SaveScore_OLD(int nr)
10325 {
10326   int i;
10327   char *filename = getScoreFilename(nr);
10328   FILE *file;
10329
10330   // used instead of "leveldir_current->subdir" (for network games)
10331   InitScoreDirectory(levelset.identifier);
10332
10333   if (!(file = fopen(filename, MODE_WRITE)))
10334   {
10335     Warn("cannot save score for level %d", nr);
10336
10337     return;
10338   }
10339
10340   fprintf(file, "%s\n\n", SCORE_COOKIE);
10341
10342   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10343     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10344
10345   fclose(file);
10346
10347   SetFilePermissions(filename, PERMS_PRIVATE);
10348 }
10349 #endif
10350
10351 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10352 {
10353   putFileVersion(file, scores->file_version);
10354   putFileVersion(file, scores->game_version);
10355 }
10356
10357 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10358 {
10359   int level_identifier_size = strlen(scores->level_identifier) + 1;
10360   int i;
10361
10362   putFile16BitBE(file, level_identifier_size);
10363
10364   for (i = 0; i < level_identifier_size; i++)
10365     putFile8Bit(file, scores->level_identifier[i]);
10366
10367   putFile16BitBE(file, scores->level_nr);
10368   putFile16BitBE(file, scores->num_entries);
10369 }
10370
10371 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10372 {
10373   int i, j;
10374
10375   for (i = 0; i < scores->num_entries; i++)
10376   {
10377     int name_size = strlen(scores->entry[i].name);
10378
10379     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10380       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10381   }
10382 }
10383
10384 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10385 {
10386   int i;
10387
10388   for (i = 0; i < scores->num_entries; i++)
10389     putFile16BitBE(file, scores->entry[i].score);
10390 }
10391
10392 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10393 {
10394   int i;
10395
10396   for (i = 0; i < scores->num_entries; i++)
10397     putFile32BitBE(file, scores->entry[i].score);
10398 }
10399
10400 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10401 {
10402   int i;
10403
10404   for (i = 0; i < scores->num_entries; i++)
10405     putFile32BitBE(file, scores->entry[i].time);
10406 }
10407
10408 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10409 {
10410   int i, j;
10411
10412   for (i = 0; i < scores->num_entries; i++)
10413   {
10414     int size = strlen(scores->entry[i].tape_basename);
10415
10416     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10417       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10418   }
10419 }
10420
10421 static void SaveScoreToFilename(char *filename)
10422 {
10423   FILE *file;
10424   int info_chunk_size;
10425   int name_chunk_size;
10426   int scor_chunk_size;
10427   int sc4r_chunk_size;
10428   int time_chunk_size;
10429   int tape_chunk_size;
10430   boolean has_large_score_values;
10431   int i;
10432
10433   if (!(file = fopen(filename, MODE_WRITE)))
10434   {
10435     Warn("cannot save score file '%s'", filename);
10436
10437     return;
10438   }
10439
10440   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10441   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10442   scor_chunk_size = scores.num_entries * 2;
10443   sc4r_chunk_size = scores.num_entries * 4;
10444   time_chunk_size = scores.num_entries * 4;
10445   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10446
10447   has_large_score_values = FALSE;
10448   for (i = 0; i < scores.num_entries; i++)
10449     if (scores.entry[i].score > 0xffff)
10450       has_large_score_values = TRUE;
10451
10452   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10453   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10454
10455   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10456   SaveScore_VERS(file, &scores);
10457
10458   putFileChunkBE(file, "INFO", info_chunk_size);
10459   SaveScore_INFO(file, &scores);
10460
10461   putFileChunkBE(file, "NAME", name_chunk_size);
10462   SaveScore_NAME(file, &scores);
10463
10464   if (has_large_score_values)
10465   {
10466     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10467     SaveScore_SC4R(file, &scores);
10468   }
10469   else
10470   {
10471     putFileChunkBE(file, "SCOR", scor_chunk_size);
10472     SaveScore_SCOR(file, &scores);
10473   }
10474
10475   putFileChunkBE(file, "TIME", time_chunk_size);
10476   SaveScore_TIME(file, &scores);
10477
10478   putFileChunkBE(file, "TAPE", tape_chunk_size);
10479   SaveScore_TAPE(file, &scores);
10480
10481   fclose(file);
10482
10483   SetFilePermissions(filename, PERMS_PRIVATE);
10484 }
10485
10486 void SaveScore(int nr)
10487 {
10488   char *filename = getScoreFilename(nr);
10489   int i;
10490
10491   // used instead of "leveldir_current->subdir" (for network games)
10492   InitScoreDirectory(levelset.identifier);
10493
10494   scores.file_version = FILE_VERSION_ACTUAL;
10495   scores.game_version = GAME_VERSION_ACTUAL;
10496
10497   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10498   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10499   scores.level_nr = level_nr;
10500
10501   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10502     if (scores.entry[i].score == 0 &&
10503         scores.entry[i].time == 0 &&
10504         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10505       break;
10506
10507   scores.num_entries = i;
10508
10509   if (scores.num_entries == 0)
10510     return;
10511
10512   SaveScoreToFilename(filename);
10513 }
10514
10515 static void LoadServerScoreFromCache(int nr)
10516 {
10517   struct ScoreEntry score_entry;
10518   struct
10519   {
10520     void *value;
10521     boolean is_string;
10522     int string_size;
10523   }
10524   score_mapping[] =
10525   {
10526     { &score_entry.score,               FALSE,  0                       },
10527     { &score_entry.time,                FALSE,  0                       },
10528     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10529     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10530     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10531     { &score_entry.id,                  FALSE,  0                       },
10532     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10533     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10534     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10535     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10536
10537     { NULL,                             FALSE,  0                       }
10538   };
10539   char *filename = getScoreCacheFilename(nr);
10540   SetupFileHash *score_hash = loadSetupFileHash(filename);
10541   int i, j;
10542
10543   server_scores.num_entries = 0;
10544
10545   if (score_hash == NULL)
10546     return;
10547
10548   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10549   {
10550     score_entry = server_scores.entry[i];
10551
10552     for (j = 0; score_mapping[j].value != NULL; j++)
10553     {
10554       char token[10];
10555
10556       sprintf(token, "%02d.%d", i, j);
10557
10558       char *value = getHashEntry(score_hash, token);
10559
10560       if (value == NULL)
10561         continue;
10562
10563       if (score_mapping[j].is_string)
10564       {
10565         char *score_value = (char *)score_mapping[j].value;
10566         int value_size = score_mapping[j].string_size;
10567
10568         strncpy(score_value, value, value_size);
10569         score_value[value_size] = '\0';
10570       }
10571       else
10572       {
10573         int *score_value = (int *)score_mapping[j].value;
10574
10575         *score_value = atoi(value);
10576       }
10577
10578       server_scores.num_entries = i + 1;
10579     }
10580
10581     server_scores.entry[i] = score_entry;
10582   }
10583
10584   freeSetupFileHash(score_hash);
10585 }
10586
10587 void LoadServerScore(int nr, boolean download_score)
10588 {
10589   if (!setup.use_api_server)
10590     return;
10591
10592   // always start with reliable default values
10593   setServerScoreInfoToDefaults();
10594
10595   // 1st step: load server scores from cache file (which may not exist)
10596   // (this should prevent reading it while the thread is writing to it)
10597   LoadServerScoreFromCache(nr);
10598
10599   if (download_score && runtime.use_api_server)
10600   {
10601     // 2nd step: download server scores from score server to cache file
10602     // (as thread, as it might time out if the server is not reachable)
10603     ApiGetScoreAsThread(nr);
10604   }
10605 }
10606
10607 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10608 {
10609   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10610
10611   // if score tape not uploaded, ask for uploading missing tapes later
10612   if (!setup.has_remaining_tapes)
10613     setup.ask_for_remaining_tapes = TRUE;
10614
10615   setup.provide_uploading_tapes = TRUE;
10616   setup.has_remaining_tapes = TRUE;
10617
10618   SaveSetup_ServerSetup();
10619 }
10620
10621 void SaveServerScore(int nr, boolean tape_saved)
10622 {
10623   if (!runtime.use_api_server)
10624   {
10625     PrepareScoreTapesForUpload(leveldir_current->subdir);
10626
10627     return;
10628   }
10629
10630   ApiAddScoreAsThread(nr, tape_saved, NULL);
10631 }
10632
10633 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10634                              char *score_tape_filename)
10635 {
10636   if (!runtime.use_api_server)
10637     return;
10638
10639   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10640 }
10641
10642 void LoadLocalAndServerScore(int nr, boolean download_score)
10643 {
10644   int last_added_local = scores.last_added_local;
10645   boolean force_last_added = scores.force_last_added;
10646
10647   // needed if only showing server scores
10648   setScoreInfoToDefaults();
10649
10650   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10651     LoadScore(nr);
10652
10653   // restore last added local score entry (before merging server scores)
10654   scores.last_added = scores.last_added_local = last_added_local;
10655
10656   if (setup.use_api_server &&
10657       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10658   {
10659     // load server scores from cache file and trigger update from server
10660     LoadServerScore(nr, download_score);
10661
10662     // merge local scores with scores from server
10663     MergeServerScore();
10664   }
10665
10666   if (force_last_added)
10667     scores.force_last_added = force_last_added;
10668 }
10669
10670
10671 // ============================================================================
10672 // setup file functions
10673 // ============================================================================
10674
10675 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10676
10677
10678 static struct TokenInfo global_setup_tokens[] =
10679 {
10680   {
10681     TYPE_STRING,
10682     &setup.player_name,                         "player_name"
10683   },
10684   {
10685     TYPE_SWITCH,
10686     &setup.multiple_users,                      "multiple_users"
10687   },
10688   {
10689     TYPE_SWITCH,
10690     &setup.sound,                               "sound"
10691   },
10692   {
10693     TYPE_SWITCH,
10694     &setup.sound_loops,                         "repeating_sound_loops"
10695   },
10696   {
10697     TYPE_SWITCH,
10698     &setup.sound_music,                         "background_music"
10699   },
10700   {
10701     TYPE_SWITCH,
10702     &setup.sound_simple,                        "simple_sound_effects"
10703   },
10704   {
10705     TYPE_SWITCH,
10706     &setup.toons,                               "toons"
10707   },
10708   {
10709     TYPE_SWITCH,
10710     &setup.global_animations,                   "global_animations"
10711   },
10712   {
10713     TYPE_SWITCH,
10714     &setup.scroll_delay,                        "scroll_delay"
10715   },
10716   {
10717     TYPE_SWITCH,
10718     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10719   },
10720   {
10721     TYPE_INTEGER,
10722     &setup.scroll_delay_value,                  "scroll_delay_value"
10723   },
10724   {
10725     TYPE_STRING,
10726     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10727   },
10728   {
10729     TYPE_INTEGER,
10730     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10731   },
10732   {
10733     TYPE_SWITCH,
10734     &setup.fade_screens,                        "fade_screens"
10735   },
10736   {
10737     TYPE_SWITCH,
10738     &setup.autorecord,                          "automatic_tape_recording"
10739   },
10740   {
10741     TYPE_SWITCH,
10742     &setup.autorecord_after_replay,             "autorecord_after_replay"
10743   },
10744   {
10745     TYPE_SWITCH,
10746     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10747   },
10748   {
10749     TYPE_SWITCH,
10750     &setup.show_titlescreen,                    "show_titlescreen"
10751   },
10752   {
10753     TYPE_SWITCH,
10754     &setup.quick_doors,                         "quick_doors"
10755   },
10756   {
10757     TYPE_SWITCH,
10758     &setup.team_mode,                           "team_mode"
10759   },
10760   {
10761     TYPE_SWITCH,
10762     &setup.handicap,                            "handicap"
10763   },
10764   {
10765     TYPE_SWITCH,
10766     &setup.skip_levels,                         "skip_levels"
10767   },
10768   {
10769     TYPE_SWITCH_3_STATES,
10770     &setup.allow_skipping_levels,               "allow_skipping_levels"
10771   },
10772   {
10773     TYPE_SWITCH,
10774     &setup.increment_levels,                    "increment_levels"
10775   },
10776   {
10777     TYPE_SWITCH,
10778     &setup.auto_play_next_level,                "auto_play_next_level"
10779   },
10780   {
10781     TYPE_SWITCH,
10782     &setup.count_score_after_game,              "count_score_after_game"
10783   },
10784   {
10785     TYPE_SWITCH,
10786     &setup.show_scores_after_game,              "show_scores_after_game"
10787   },
10788   {
10789     TYPE_SWITCH,
10790     &setup.time_limit,                          "time_limit"
10791   },
10792   {
10793     TYPE_SWITCH,
10794     &setup.fullscreen,                          "fullscreen"
10795   },
10796   {
10797     TYPE_INTEGER,
10798     &setup.window_scaling_percent,              "window_scaling_percent"
10799   },
10800   {
10801     TYPE_STRING,
10802     &setup.window_scaling_quality,              "window_scaling_quality"
10803   },
10804   {
10805     TYPE_STRING,
10806     &setup.screen_rendering_mode,               "screen_rendering_mode"
10807   },
10808   {
10809     TYPE_STRING,
10810     &setup.vsync_mode,                          "vsync_mode"
10811   },
10812   {
10813     TYPE_SWITCH,
10814     &setup.ask_on_escape,                       "ask_on_escape"
10815   },
10816   {
10817     TYPE_SWITCH,
10818     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10819   },
10820   {
10821     TYPE_SWITCH,
10822     &setup.ask_on_game_over,                    "ask_on_game_over"
10823   },
10824   {
10825     TYPE_SWITCH,
10826     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10827   },
10828   {
10829     TYPE_SWITCH,
10830     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10831   },
10832   {
10833     TYPE_SWITCH,
10834     &setup.quick_switch,                        "quick_player_switch"
10835   },
10836   {
10837     TYPE_SWITCH,
10838     &setup.input_on_focus,                      "input_on_focus"
10839   },
10840   {
10841     TYPE_SWITCH,
10842     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10843   },
10844   {
10845     TYPE_SWITCH,
10846     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10847   },
10848   {
10849     TYPE_SWITCH,
10850     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10851   },
10852   {
10853     TYPE_SWITCH,
10854     &setup.game_speed_extended,                 "game_speed_extended"
10855   },
10856   {
10857     TYPE_INTEGER,
10858     &setup.game_frame_delay,                    "game_frame_delay"
10859   },
10860   {
10861     TYPE_INTEGER,
10862     &setup.default_game_engine_type,            "default_game_engine_type"
10863   },
10864   {
10865     TYPE_SWITCH,
10866     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10867   },
10868   {
10869     TYPE_SWITCH,
10870     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10871   },
10872   {
10873     TYPE_SWITCH,
10874     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10875   },
10876   {
10877     TYPE_SWITCH,
10878     &setup.bd_show_invisible_outbox,            "bd_show_invisible_outbox"
10879   },
10880   {
10881     TYPE_SWITCH_3_STATES,
10882     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10883   },
10884   {
10885     TYPE_SWITCH_3_STATES,
10886     &setup.bd_pushing_graphics,                 "bd_pushing_graphics"
10887   },
10888   {
10889     TYPE_SWITCH_3_STATES,
10890     &setup.bd_up_down_graphics,                 "bd_up_down_graphics"
10891   },
10892   {
10893     TYPE_SWITCH_3_STATES,
10894     &setup.bd_falling_sounds,                   "bd_falling_sounds"
10895   },
10896   {
10897     TYPE_INTEGER,
10898     &setup.bd_palette_c64,                      "bd_palette_c64"
10899   },
10900   {
10901     TYPE_INTEGER,
10902     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10903   },
10904   {
10905     TYPE_INTEGER,
10906     &setup.bd_palette_atari,                    "bd_palette_atari"
10907   },
10908   {
10909     TYPE_INTEGER,
10910     &setup.bd_default_color_type,               "bd_default_color_type"
10911   },
10912   {
10913     TYPE_SWITCH,
10914     &setup.bd_random_colors,                    "bd_random_colors"
10915   },
10916   {
10917     TYPE_SWITCH,
10918     &setup.sp_show_border_elements,             "sp_show_border_elements"
10919   },
10920   {
10921     TYPE_SWITCH,
10922     &setup.small_game_graphics,                 "small_game_graphics"
10923   },
10924   {
10925     TYPE_SWITCH,
10926     &setup.show_load_save_buttons,              "show_load_save_buttons"
10927   },
10928   {
10929     TYPE_SWITCH,
10930     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10931   },
10932   {
10933     TYPE_STRING,
10934     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10935   },
10936   {
10937     TYPE_STRING,
10938     &setup.graphics_set,                        "graphics_set"
10939   },
10940   {
10941     TYPE_STRING,
10942     &setup.sounds_set,                          "sounds_set"
10943   },
10944   {
10945     TYPE_STRING,
10946     &setup.music_set,                           "music_set"
10947   },
10948   {
10949     TYPE_SWITCH_3_STATES,
10950     &setup.override_level_graphics,             "override_level_graphics"
10951   },
10952   {
10953     TYPE_SWITCH_3_STATES,
10954     &setup.override_level_sounds,               "override_level_sounds"
10955   },
10956   {
10957     TYPE_SWITCH_3_STATES,
10958     &setup.override_level_music,                "override_level_music"
10959   },
10960   {
10961     TYPE_INTEGER,
10962     &setup.volume_simple,                       "volume_simple"
10963   },
10964   {
10965     TYPE_INTEGER,
10966     &setup.volume_loops,                        "volume_loops"
10967   },
10968   {
10969     TYPE_INTEGER,
10970     &setup.volume_music,                        "volume_music"
10971   },
10972   {
10973     TYPE_SWITCH,
10974     &setup.audio_sample_rate_44100,             "audio_sample_rate_44100"
10975   },
10976   {
10977     TYPE_SWITCH,
10978     &setup.network_mode,                        "network_mode"
10979   },
10980   {
10981     TYPE_PLAYER,
10982     &setup.network_player_nr,                   "network_player"
10983   },
10984   {
10985     TYPE_STRING,
10986     &setup.network_server_hostname,             "network_server_hostname"
10987   },
10988   {
10989     TYPE_STRING,
10990     &setup.touch.control_type,                  "touch.control_type"
10991   },
10992   {
10993     TYPE_INTEGER,
10994     &setup.touch.move_distance,                 "touch.move_distance"
10995   },
10996   {
10997     TYPE_INTEGER,
10998     &setup.touch.drop_distance,                 "touch.drop_distance"
10999   },
11000   {
11001     TYPE_INTEGER,
11002     &setup.touch.transparency,                  "touch.transparency"
11003   },
11004   {
11005     TYPE_INTEGER,
11006     &setup.touch.draw_outlined,                 "touch.draw_outlined"
11007   },
11008   {
11009     TYPE_INTEGER,
11010     &setup.touch.draw_pressed,                  "touch.draw_pressed"
11011   },
11012   {
11013     TYPE_INTEGER,
11014     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
11015   },
11016   {
11017     TYPE_INTEGER,
11018     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
11019   },
11020   {
11021     TYPE_INTEGER,
11022     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
11023   },
11024   {
11025     TYPE_INTEGER,
11026     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
11027   },
11028   {
11029     TYPE_SWITCH,
11030     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
11031   },
11032 };
11033
11034 static struct TokenInfo auto_setup_tokens[] =
11035 {
11036   {
11037     TYPE_INTEGER,
11038     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
11039   },
11040 };
11041
11042 static struct TokenInfo server_setup_tokens[] =
11043 {
11044   {
11045     TYPE_STRING,
11046     &setup.player_uuid,                         "player_uuid"
11047   },
11048   {
11049     TYPE_INTEGER,
11050     &setup.player_version,                      "player_version"
11051   },
11052   {
11053     TYPE_SWITCH,
11054     &setup.use_api_server,          TEST_PREFIX "use_api_server"
11055   },
11056   {
11057     TYPE_STRING,
11058     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
11059   },
11060   {
11061     TYPE_STRING,
11062     &setup.api_server_password,     TEST_PREFIX "api_server_password"
11063   },
11064   {
11065     TYPE_SWITCH,
11066     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
11067   },
11068   {
11069     TYPE_SWITCH,
11070     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
11071   },
11072   {
11073     TYPE_SWITCH,
11074     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
11075   },
11076   {
11077     TYPE_SWITCH,
11078     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
11079   },
11080   {
11081     TYPE_SWITCH,
11082     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
11083   },
11084 };
11085
11086 static struct TokenInfo editor_setup_tokens[] =
11087 {
11088   {
11089     TYPE_SWITCH,
11090     &setup.editor.el_classic,                   "editor.el_classic"
11091   },
11092   {
11093     TYPE_SWITCH,
11094     &setup.editor.el_custom,                    "editor.el_custom"
11095   },
11096   {
11097     TYPE_SWITCH,
11098     &setup.editor.el_user_defined,              "editor.el_user_defined"
11099   },
11100   {
11101     TYPE_SWITCH,
11102     &setup.editor.el_dynamic,                   "editor.el_dynamic"
11103   },
11104   {
11105     TYPE_SWITCH,
11106     &setup.editor.el_headlines,                 "editor.el_headlines"
11107   },
11108   {
11109     TYPE_SWITCH,
11110     &setup.editor.show_element_token,           "editor.show_element_token"
11111   },
11112   {
11113     TYPE_SWITCH,
11114     &setup.editor.fast_game_start,              "editor.fast_game_start"
11115   },
11116   {
11117     TYPE_SWITCH,
11118     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
11119   },
11120 };
11121
11122 static struct TokenInfo editor_cascade_setup_tokens[] =
11123 {
11124   {
11125     TYPE_SWITCH,
11126     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
11127   },
11128   {
11129     TYPE_SWITCH,
11130     &setup.editor_cascade.el_bdx,               "editor.cascade.el_bdx"
11131   },
11132   {
11133     TYPE_SWITCH,
11134     &setup.editor_cascade.el_bdx_effects,       "editor.cascade.el_bdx_effects"
11135   },
11136   {
11137     TYPE_SWITCH,
11138     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
11139   },
11140   {
11141     TYPE_SWITCH,
11142     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
11143   },
11144   {
11145     TYPE_SWITCH,
11146     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
11147   },
11148   {
11149     TYPE_SWITCH,
11150     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
11151   },
11152   {
11153     TYPE_SWITCH,
11154     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11155   },
11156   {
11157     TYPE_SWITCH,
11158     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11159   },
11160   {
11161     TYPE_SWITCH,
11162     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11163   },
11164   {
11165     TYPE_SWITCH,
11166     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11167   },
11168   {
11169     TYPE_SWITCH,
11170     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11171   },
11172   {
11173     TYPE_SWITCH,
11174     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11175   },
11176   {
11177     TYPE_SWITCH,
11178     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11179   },
11180   {
11181     TYPE_SWITCH,
11182     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11183   },
11184   {
11185     TYPE_SWITCH,
11186     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11187   },
11188   {
11189     TYPE_SWITCH,
11190     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11191   },
11192   {
11193     TYPE_SWITCH,
11194     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11195   },
11196   {
11197     TYPE_SWITCH,
11198     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11199   },
11200   {
11201     TYPE_SWITCH,
11202     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11203   },
11204 };
11205
11206 static struct TokenInfo shortcut_setup_tokens[] =
11207 {
11208   {
11209     TYPE_KEY_X11,
11210     &setup.shortcut.save_game,                  "shortcut.save_game"
11211   },
11212   {
11213     TYPE_KEY_X11,
11214     &setup.shortcut.load_game,                  "shortcut.load_game"
11215   },
11216   {
11217     TYPE_KEY_X11,
11218     &setup.shortcut.restart_game,               "shortcut.restart_game"
11219   },
11220   {
11221     TYPE_KEY_X11,
11222     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11223   },
11224   {
11225     TYPE_KEY_X11,
11226     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11227   },
11228   {
11229     TYPE_KEY_X11,
11230     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11231   },
11232   {
11233     TYPE_KEY_X11,
11234     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11235   },
11236   {
11237     TYPE_KEY_X11,
11238     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11239   },
11240   {
11241     TYPE_KEY_X11,
11242     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11243   },
11244   {
11245     TYPE_KEY_X11,
11246     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11247   },
11248   {
11249     TYPE_KEY_X11,
11250     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11251   },
11252   {
11253     TYPE_KEY_X11,
11254     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11255   },
11256   {
11257     TYPE_KEY_X11,
11258     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11259   },
11260   {
11261     TYPE_KEY_X11,
11262     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11263   },
11264   {
11265     TYPE_KEY_X11,
11266     &setup.shortcut.tape_record,                "shortcut.tape_record"
11267   },
11268   {
11269     TYPE_KEY_X11,
11270     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11271   },
11272   {
11273     TYPE_KEY_X11,
11274     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11275   },
11276   {
11277     TYPE_KEY_X11,
11278     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11279   },
11280   {
11281     TYPE_KEY_X11,
11282     &setup.shortcut.sound_music,                "shortcut.sound_music"
11283   },
11284   {
11285     TYPE_KEY_X11,
11286     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11287   },
11288   {
11289     TYPE_KEY_X11,
11290     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11291   },
11292   {
11293     TYPE_KEY_X11,
11294     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11295   },
11296   {
11297     TYPE_KEY_X11,
11298     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11299   },
11300   {
11301     TYPE_KEY_X11,
11302     &setup.shortcut.speed_fast,                 "shortcut.speed_fast"
11303   },
11304   {
11305     TYPE_KEY_X11,
11306     &setup.shortcut.speed_slow,                 "shortcut.speed_slow"
11307   },
11308 };
11309
11310 static struct SetupInputInfo setup_input;
11311 static struct TokenInfo player_setup_tokens[] =
11312 {
11313   {
11314     TYPE_BOOLEAN,
11315     &setup_input.use_joystick,                  ".use_joystick"
11316   },
11317   {
11318     TYPE_STRING,
11319     &setup_input.joy.device_name,               ".joy.device_name"
11320   },
11321   {
11322     TYPE_INTEGER,
11323     &setup_input.joy.xleft,                     ".joy.xleft"
11324   },
11325   {
11326     TYPE_INTEGER,
11327     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11328   },
11329   {
11330     TYPE_INTEGER,
11331     &setup_input.joy.xright,                    ".joy.xright"
11332   },
11333   {
11334     TYPE_INTEGER,
11335     &setup_input.joy.yupper,                    ".joy.yupper"
11336   },
11337   {
11338     TYPE_INTEGER,
11339     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11340   },
11341   {
11342     TYPE_INTEGER,
11343     &setup_input.joy.ylower,                    ".joy.ylower"
11344   },
11345   {
11346     TYPE_INTEGER,
11347     &setup_input.joy.snap,                      ".joy.snap_field"
11348   },
11349   {
11350     TYPE_INTEGER,
11351     &setup_input.joy.drop,                      ".joy.place_bomb"
11352   },
11353   {
11354     TYPE_KEY_X11,
11355     &setup_input.key.left,                      ".key.move_left"
11356   },
11357   {
11358     TYPE_KEY_X11,
11359     &setup_input.key.right,                     ".key.move_right"
11360   },
11361   {
11362     TYPE_KEY_X11,
11363     &setup_input.key.up,                        ".key.move_up"
11364   },
11365   {
11366     TYPE_KEY_X11,
11367     &setup_input.key.down,                      ".key.move_down"
11368   },
11369   {
11370     TYPE_KEY_X11,
11371     &setup_input.key.snap,                      ".key.snap_field"
11372   },
11373   {
11374     TYPE_KEY_X11,
11375     &setup_input.key.drop,                      ".key.place_bomb"
11376   },
11377 };
11378
11379 static struct TokenInfo system_setup_tokens[] =
11380 {
11381   {
11382     TYPE_STRING,
11383     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11384   },
11385   {
11386     TYPE_STRING,
11387     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11388   },
11389   {
11390     TYPE_STRING,
11391     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11392   },
11393   {
11394     TYPE_INTEGER,
11395     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11396   },
11397 };
11398
11399 static struct TokenInfo internal_setup_tokens[] =
11400 {
11401   {
11402     TYPE_STRING,
11403     &setup.internal.program_title,              "program_title"
11404   },
11405   {
11406     TYPE_STRING,
11407     &setup.internal.program_version,            "program_version"
11408   },
11409   {
11410     TYPE_STRING,
11411     &setup.internal.program_author,             "program_author"
11412   },
11413   {
11414     TYPE_STRING,
11415     &setup.internal.program_email,              "program_email"
11416   },
11417   {
11418     TYPE_STRING,
11419     &setup.internal.program_website,            "program_website"
11420   },
11421   {
11422     TYPE_STRING,
11423     &setup.internal.program_copyright,          "program_copyright"
11424   },
11425   {
11426     TYPE_STRING,
11427     &setup.internal.program_company,            "program_company"
11428   },
11429   {
11430     TYPE_STRING,
11431     &setup.internal.program_icon_file,          "program_icon_file"
11432   },
11433   {
11434     TYPE_STRING,
11435     &setup.internal.default_graphics_set,       "default_graphics_set"
11436   },
11437   {
11438     TYPE_STRING,
11439     &setup.internal.default_sounds_set,         "default_sounds_set"
11440   },
11441   {
11442     TYPE_STRING,
11443     &setup.internal.default_music_set,          "default_music_set"
11444   },
11445   {
11446     TYPE_STRING,
11447     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11448   },
11449   {
11450     TYPE_STRING,
11451     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11452   },
11453   {
11454     TYPE_STRING,
11455     &setup.internal.fallback_music_file,        "fallback_music_file"
11456   },
11457   {
11458     TYPE_STRING,
11459     &setup.internal.default_level_series,       "default_level_series"
11460   },
11461   {
11462     TYPE_INTEGER,
11463     &setup.internal.default_window_width,       "default_window_width"
11464   },
11465   {
11466     TYPE_INTEGER,
11467     &setup.internal.default_window_height,      "default_window_height"
11468   },
11469   {
11470     TYPE_BOOLEAN,
11471     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11472   },
11473   {
11474     TYPE_BOOLEAN,
11475     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11476   },
11477   {
11478     TYPE_BOOLEAN,
11479     &setup.internal.create_user_levelset,       "create_user_levelset"
11480   },
11481   {
11482     TYPE_BOOLEAN,
11483     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11484   },
11485   {
11486     TYPE_BOOLEAN,
11487     &setup.internal.menu_game,                  "menu_game"
11488   },
11489   {
11490     TYPE_BOOLEAN,
11491     &setup.internal.menu_engines,               "menu_engines"
11492   },
11493   {
11494     TYPE_BOOLEAN,
11495     &setup.internal.menu_editor,                "menu_editor"
11496   },
11497   {
11498     TYPE_BOOLEAN,
11499     &setup.internal.menu_graphics,              "menu_graphics"
11500   },
11501   {
11502     TYPE_BOOLEAN,
11503     &setup.internal.menu_sound,                 "menu_sound"
11504   },
11505   {
11506     TYPE_BOOLEAN,
11507     &setup.internal.menu_artwork,               "menu_artwork"
11508   },
11509   {
11510     TYPE_BOOLEAN,
11511     &setup.internal.menu_input,                 "menu_input"
11512   },
11513   {
11514     TYPE_BOOLEAN,
11515     &setup.internal.menu_touch,                 "menu_touch"
11516   },
11517   {
11518     TYPE_BOOLEAN,
11519     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11520   },
11521   {
11522     TYPE_BOOLEAN,
11523     &setup.internal.menu_exit,                  "menu_exit"
11524   },
11525   {
11526     TYPE_BOOLEAN,
11527     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11528   },
11529   {
11530     TYPE_BOOLEAN,
11531     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11532   },
11533   {
11534     TYPE_BOOLEAN,
11535     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11536   },
11537   {
11538     TYPE_BOOLEAN,
11539     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11540   },
11541   {
11542     TYPE_BOOLEAN,
11543     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11544   },
11545   {
11546     TYPE_BOOLEAN,
11547     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11548   },
11549   {
11550     TYPE_BOOLEAN,
11551     &setup.internal.menu_shortcuts_speed,       "menu_shortcuts_speed"
11552   },
11553   {
11554     TYPE_BOOLEAN,
11555     &setup.internal.info_title,                 "info_title"
11556   },
11557   {
11558     TYPE_BOOLEAN,
11559     &setup.internal.info_elements,              "info_elements"
11560   },
11561   {
11562     TYPE_BOOLEAN,
11563     &setup.internal.info_music,                 "info_music"
11564   },
11565   {
11566     TYPE_BOOLEAN,
11567     &setup.internal.info_credits,               "info_credits"
11568   },
11569   {
11570     TYPE_BOOLEAN,
11571     &setup.internal.info_program,               "info_program"
11572   },
11573   {
11574     TYPE_BOOLEAN,
11575     &setup.internal.info_version,               "info_version"
11576   },
11577   {
11578     TYPE_BOOLEAN,
11579     &setup.internal.info_levelset,              "info_levelset"
11580   },
11581   {
11582     TYPE_BOOLEAN,
11583     &setup.internal.info_exit,                  "info_exit"
11584   },
11585 };
11586
11587 static struct TokenInfo debug_setup_tokens[] =
11588 {
11589   {
11590     TYPE_INTEGER,
11591     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11592   },
11593   {
11594     TYPE_INTEGER,
11595     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11596   },
11597   {
11598     TYPE_INTEGER,
11599     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11600   },
11601   {
11602     TYPE_INTEGER,
11603     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11604   },
11605   {
11606     TYPE_INTEGER,
11607     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11608   },
11609   {
11610     TYPE_INTEGER,
11611     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11612   },
11613   {
11614     TYPE_INTEGER,
11615     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11616   },
11617   {
11618     TYPE_INTEGER,
11619     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11620   },
11621   {
11622     TYPE_INTEGER,
11623     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11624   },
11625   {
11626     TYPE_INTEGER,
11627     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11628   },
11629   {
11630     TYPE_KEY_X11,
11631     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11632   },
11633   {
11634     TYPE_KEY_X11,
11635     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11636   },
11637   {
11638     TYPE_KEY_X11,
11639     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11640   },
11641   {
11642     TYPE_KEY_X11,
11643     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11644   },
11645   {
11646     TYPE_KEY_X11,
11647     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11648   },
11649   {
11650     TYPE_KEY_X11,
11651     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11652   },
11653   {
11654     TYPE_KEY_X11,
11655     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11656   },
11657   {
11658     TYPE_KEY_X11,
11659     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11660   },
11661   {
11662     TYPE_KEY_X11,
11663     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11664   },
11665   {
11666     TYPE_KEY_X11,
11667     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11668   },
11669   {
11670     TYPE_BOOLEAN,
11671     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11672   {
11673     TYPE_BOOLEAN,
11674     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11675   },
11676   {
11677     TYPE_BOOLEAN,
11678     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11679   },
11680   {
11681     TYPE_SWITCH_3_STATES,
11682     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11683   },
11684   {
11685     TYPE_INTEGER,
11686     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11687   },
11688 };
11689
11690 static struct TokenInfo options_setup_tokens[] =
11691 {
11692   {
11693     TYPE_BOOLEAN,
11694     &setup.options.verbose,                     "options.verbose"
11695   },
11696   {
11697     TYPE_BOOLEAN,
11698     &setup.options.debug,                       "options.debug"
11699   },
11700   {
11701     TYPE_STRING,
11702     &setup.options.debug_mode,                  "options.debug_mode"
11703   },
11704 };
11705
11706 static void setSetupInfoToDefaults(struct SetupInfo *si)
11707 {
11708   int i;
11709
11710   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11711
11712   si->multiple_users = TRUE;
11713
11714   si->sound = TRUE;
11715   si->sound_loops = TRUE;
11716   si->sound_music = TRUE;
11717   si->sound_simple = TRUE;
11718   si->toons = TRUE;
11719   si->global_animations = TRUE;
11720   si->scroll_delay = TRUE;
11721   si->forced_scroll_delay = FALSE;
11722   si->scroll_delay_value = STD_SCROLL_DELAY;
11723   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11724   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11725   si->fade_screens = TRUE;
11726   si->autorecord = TRUE;
11727   si->autorecord_after_replay = TRUE;
11728   si->auto_pause_on_start = FALSE;
11729   si->show_titlescreen = TRUE;
11730   si->quick_doors = FALSE;
11731   si->team_mode = FALSE;
11732   si->handicap = TRUE;
11733   si->skip_levels = TRUE;
11734   si->allow_skipping_levels = STATE_ASK;
11735   si->increment_levels = TRUE;
11736   si->auto_play_next_level = TRUE;
11737   si->count_score_after_game = TRUE;
11738   si->show_scores_after_game = TRUE;
11739   si->time_limit = TRUE;
11740   si->fullscreen = FALSE;
11741   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11742   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11743   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11744   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11745   si->ask_on_escape = TRUE;
11746   si->ask_on_escape_editor = TRUE;
11747   si->ask_on_game_over = TRUE;
11748   si->ask_on_quit_game = TRUE;
11749   si->ask_on_quit_program = TRUE;
11750   si->quick_switch = FALSE;
11751   si->input_on_focus = FALSE;
11752   si->prefer_aga_graphics = TRUE;
11753   si->prefer_lowpass_sounds = FALSE;
11754   si->prefer_extra_panel_items = TRUE;
11755   si->game_speed_extended = FALSE;
11756   si->game_frame_delay = GAME_FRAME_DELAY;
11757   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11758   si->bd_skip_uncovering = FALSE;
11759   si->bd_skip_hatching = FALSE;
11760   si->bd_scroll_delay = TRUE;
11761   si->bd_show_invisible_outbox = FALSE;
11762   si->bd_smooth_movements = STATE_TRUE;
11763   si->bd_pushing_graphics = STATE_TRUE;
11764   si->bd_up_down_graphics = STATE_TRUE;
11765   si->bd_falling_sounds = STATE_AUTO;
11766   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11767   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11768   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11769   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11770   si->bd_random_colors = FALSE;
11771   si->sp_show_border_elements = FALSE;
11772   si->small_game_graphics = FALSE;
11773   si->show_load_save_buttons = FALSE;
11774   si->show_undo_redo_buttons = FALSE;
11775   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11776
11777   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11778   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11779   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11780
11781   si->override_level_graphics = STATE_FALSE;
11782   si->override_level_sounds = STATE_FALSE;
11783   si->override_level_music = STATE_FALSE;
11784
11785   si->volume_simple = 100;              // percent
11786   si->volume_loops = 100;               // percent
11787   si->volume_music = 100;               // percent
11788   si->audio_sample_rate_44100 = FALSE;
11789
11790   si->network_mode = FALSE;
11791   si->network_player_nr = 0;            // first player
11792   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11793
11794   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11795   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11796   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11797   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11798   si->touch.draw_outlined = TRUE;
11799   si->touch.draw_pressed = TRUE;
11800
11801   for (i = 0; i < 2; i++)
11802   {
11803     char *default_grid_button[6][2] =
11804     {
11805       { "      ", "  ^^  " },
11806       { "      ", "  ^^  " },
11807       { "      ", "<<  >>" },
11808       { "      ", "<<  >>" },
11809       { "111222", "  vv  " },
11810       { "111222", "  vv  " }
11811     };
11812     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11813     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11814     int min_xsize = MIN(6, grid_xsize);
11815     int min_ysize = MIN(6, grid_ysize);
11816     int startx = grid_xsize - min_xsize;
11817     int starty = grid_ysize - min_ysize;
11818     int x, y;
11819
11820     // virtual buttons grid can only be set to defaults if video is initialized
11821     // (this will be repeated if virtual buttons are not loaded from setup file)
11822     if (video.initialized)
11823     {
11824       si->touch.grid_xsize[i] = grid_xsize;
11825       si->touch.grid_ysize[i] = grid_ysize;
11826     }
11827     else
11828     {
11829       si->touch.grid_xsize[i] = -1;
11830       si->touch.grid_ysize[i] = -1;
11831     }
11832
11833     for (x = 0; x < MAX_GRID_XSIZE; x++)
11834       for (y = 0; y < MAX_GRID_YSIZE; y++)
11835         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11836
11837     for (x = 0; x < min_xsize; x++)
11838       for (y = 0; y < min_ysize; y++)
11839         si->touch.grid_button[i][x][starty + y] =
11840           default_grid_button[y][0][x];
11841
11842     for (x = 0; x < min_xsize; x++)
11843       for (y = 0; y < min_ysize; y++)
11844         si->touch.grid_button[i][startx + x][starty + y] =
11845           default_grid_button[y][1][x];
11846   }
11847
11848   si->touch.grid_initialized            = video.initialized;
11849
11850   si->touch.overlay_buttons             = FALSE;
11851
11852   si->editor.el_boulderdash             = TRUE;
11853   si->editor.el_boulderdash_native      = TRUE;
11854   si->editor.el_boulderdash_effects     = TRUE;
11855   si->editor.el_emerald_mine            = TRUE;
11856   si->editor.el_emerald_mine_club       = TRUE;
11857   si->editor.el_more                    = TRUE;
11858   si->editor.el_sokoban                 = TRUE;
11859   si->editor.el_supaplex                = TRUE;
11860   si->editor.el_diamond_caves           = TRUE;
11861   si->editor.el_dx_boulderdash          = TRUE;
11862
11863   si->editor.el_mirror_magic            = TRUE;
11864   si->editor.el_deflektor               = TRUE;
11865
11866   si->editor.el_chars                   = TRUE;
11867   si->editor.el_steel_chars             = TRUE;
11868
11869   si->editor.el_classic                 = TRUE;
11870   si->editor.el_custom                  = TRUE;
11871
11872   si->editor.el_user_defined            = FALSE;
11873   si->editor.el_dynamic                 = TRUE;
11874
11875   si->editor.el_headlines               = TRUE;
11876
11877   si->editor.show_element_token         = FALSE;
11878   si->editor.fast_game_start            = FALSE;
11879
11880   si->editor.show_read_only_warning     = TRUE;
11881
11882   si->editor.use_template_for_new_levels = TRUE;
11883
11884   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11885   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11886   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11887   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11888   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11889
11890   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11891   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11892   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11893   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11894   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11895
11896   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11897   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11898   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11899   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11900   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11901   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11902
11903   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11904   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11905   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11906
11907   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11908   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11909   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11910   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11911
11912   si->shortcut.speed_fast       = DEFAULT_KEY_SPEED_FAST;
11913   si->shortcut.speed_slow       = DEFAULT_KEY_SPEED_SLOW;
11914
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916   {
11917     si->input[i].use_joystick = FALSE;
11918     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11919     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11920     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11921     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11922     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11923     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11924     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11925     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11926     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11927     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11928     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11929     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11930     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11931     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11932     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11933   }
11934
11935   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11936   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11937   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11938   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11939
11940   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11941   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11942   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11943   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11944   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11945   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11946   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11947
11948   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11949
11950   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11951   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11952   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11953
11954   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11955   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11956   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11957
11958   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11959   si->internal.choose_from_top_leveldir = FALSE;
11960   si->internal.show_scaling_in_title = TRUE;
11961   si->internal.create_user_levelset = TRUE;
11962   si->internal.info_screens_from_main = FALSE;
11963
11964   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11965   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11966
11967   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11968   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11969   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11970   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11971   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11972   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11973   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11974   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11975   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11976   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11977
11978   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11979   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11980   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11981   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11982   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11983   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11984   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11985   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11986   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11987   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11988
11989   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11990   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11991
11992   si->debug.show_frames_per_second = FALSE;
11993
11994   si->debug.xsn_mode = STATE_AUTO;
11995   si->debug.xsn_percent = 0;
11996
11997   si->options.verbose = FALSE;
11998   si->options.debug = FALSE;
11999   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
12000
12001 #if defined(PLATFORM_ANDROID)
12002   si->fullscreen = TRUE;
12003   si->touch.overlay_buttons = TRUE;
12004 #endif
12005
12006   setHideSetupEntry(&setup.debug.xsn_mode);
12007 }
12008
12009 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
12010 {
12011   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
12012 }
12013
12014 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
12015 {
12016   si->player_uuid = NULL;       // (will be set later)
12017   si->player_version = 1;       // (will be set later)
12018
12019   si->use_api_server = TRUE;
12020   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
12021   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
12022   si->ask_for_uploading_tapes = TRUE;
12023   si->ask_for_remaining_tapes = FALSE;
12024   si->provide_uploading_tapes = TRUE;
12025   si->ask_for_using_api_server = TRUE;
12026   si->has_remaining_tapes = FALSE;
12027 }
12028
12029 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
12030 {
12031   si->editor_cascade.el_bd              = TRUE;
12032   si->editor_cascade.el_bdx             = TRUE;
12033   si->editor_cascade.el_bdx_effects     = FALSE;
12034   si->editor_cascade.el_em              = TRUE;
12035   si->editor_cascade.el_emc             = TRUE;
12036   si->editor_cascade.el_rnd             = TRUE;
12037   si->editor_cascade.el_sb              = TRUE;
12038   si->editor_cascade.el_sp              = TRUE;
12039   si->editor_cascade.el_dc              = TRUE;
12040   si->editor_cascade.el_dx              = TRUE;
12041
12042   si->editor_cascade.el_mm              = TRUE;
12043   si->editor_cascade.el_df              = TRUE;
12044
12045   si->editor_cascade.el_chars           = FALSE;
12046   si->editor_cascade.el_steel_chars     = FALSE;
12047   si->editor_cascade.el_ce              = FALSE;
12048   si->editor_cascade.el_ge              = FALSE;
12049   si->editor_cascade.el_es              = FALSE;
12050   si->editor_cascade.el_ref             = FALSE;
12051   si->editor_cascade.el_user            = FALSE;
12052   si->editor_cascade.el_dynamic         = FALSE;
12053 }
12054
12055 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
12056
12057 static char *getHideSetupToken(void *setup_value)
12058 {
12059   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
12060
12061   if (setup_value != NULL)
12062     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
12063
12064   return hide_setup_token;
12065 }
12066
12067 void setHideSetupEntry(void *setup_value)
12068 {
12069   char *hide_setup_token = getHideSetupToken(setup_value);
12070
12071   if (hide_setup_hash == NULL)
12072     hide_setup_hash = newSetupFileHash();
12073
12074   if (setup_value != NULL)
12075     setHashEntry(hide_setup_hash, hide_setup_token, "");
12076 }
12077
12078 void removeHideSetupEntry(void *setup_value)
12079 {
12080   char *hide_setup_token = getHideSetupToken(setup_value);
12081
12082   if (setup_value != NULL)
12083     removeHashEntry(hide_setup_hash, hide_setup_token);
12084 }
12085
12086 boolean hideSetupEntry(void *setup_value)
12087 {
12088   char *hide_setup_token = getHideSetupToken(setup_value);
12089
12090   return (setup_value != NULL &&
12091           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
12092 }
12093
12094 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
12095                                       struct TokenInfo *token_info,
12096                                       int token_nr, char *token_text)
12097 {
12098   char *token_hide_text = getStringCat2(token_text, ".hide");
12099   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
12100
12101   // set the value of this setup option in the setup option structure
12102   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
12103
12104   // check if this setup option should be hidden in the setup menu
12105   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
12106     setHideSetupEntry(token_info[token_nr].value);
12107
12108   free(token_hide_text);
12109 }
12110
12111 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
12112                                       struct TokenInfo *token_info,
12113                                       int token_nr)
12114 {
12115   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
12116                             token_info[token_nr].text);
12117 }
12118
12119 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
12120 {
12121   int i, pnr;
12122
12123   if (!setup_file_hash)
12124     return;
12125
12126   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12127     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
12128
12129   setup.touch.grid_initialized = TRUE;
12130   for (i = 0; i < 2; i++)
12131   {
12132     int grid_xsize = setup.touch.grid_xsize[i];
12133     int grid_ysize = setup.touch.grid_ysize[i];
12134     int x, y;
12135
12136     // if virtual buttons are not loaded from setup file, repeat initializing
12137     // virtual buttons grid with default values later when video is initialized
12138     if (grid_xsize == -1 ||
12139         grid_ysize == -1)
12140     {
12141       setup.touch.grid_initialized = FALSE;
12142
12143       continue;
12144     }
12145
12146     for (y = 0; y < grid_ysize; y++)
12147     {
12148       char token_string[MAX_LINE_LEN];
12149
12150       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12151
12152       char *value_string = getHashEntry(setup_file_hash, token_string);
12153
12154       if (value_string == NULL)
12155         continue;
12156
12157       for (x = 0; x < grid_xsize; x++)
12158       {
12159         char c = value_string[x];
12160
12161         setup.touch.grid_button[i][x][y] =
12162           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
12163       }
12164     }
12165   }
12166
12167   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12168     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
12169
12170   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12171     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
12172
12173   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12174   {
12175     char prefix[30];
12176
12177     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12178
12179     setup_input = setup.input[pnr];
12180     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12181     {
12182       char full_token[100];
12183
12184       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12185       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12186                                 full_token);
12187     }
12188     setup.input[pnr] = setup_input;
12189   }
12190
12191   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12192     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12193
12194   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12195     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12196
12197   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12198     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12199
12200   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12201     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12202
12203   setHideRelatedSetupEntries();
12204 }
12205
12206 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12207 {
12208   int i;
12209
12210   if (!setup_file_hash)
12211     return;
12212
12213   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12214     setSetupInfo(auto_setup_tokens, i,
12215                  getHashEntry(setup_file_hash,
12216                               auto_setup_tokens[i].text));
12217 }
12218
12219 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12220 {
12221   int i;
12222
12223   if (!setup_file_hash)
12224     return;
12225
12226   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12227     setSetupInfo(server_setup_tokens, i,
12228                  getHashEntry(setup_file_hash,
12229                               server_setup_tokens[i].text));
12230 }
12231
12232 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12233 {
12234   int i;
12235
12236   if (!setup_file_hash)
12237     return;
12238
12239   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12240     setSetupInfo(editor_cascade_setup_tokens, i,
12241                  getHashEntry(setup_file_hash,
12242                               editor_cascade_setup_tokens[i].text));
12243 }
12244
12245 void LoadUserNames(void)
12246 {
12247   int last_user_nr = user.nr;
12248   int i;
12249
12250   if (global.user_names != NULL)
12251   {
12252     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12253       checked_free(global.user_names[i]);
12254
12255     checked_free(global.user_names);
12256   }
12257
12258   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12259
12260   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12261   {
12262     user.nr = i;
12263
12264     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12265
12266     if (setup_file_hash)
12267     {
12268       char *player_name = getHashEntry(setup_file_hash, "player_name");
12269
12270       global.user_names[i] = getFixedUserName(player_name);
12271
12272       freeSetupFileHash(setup_file_hash);
12273     }
12274
12275     if (global.user_names[i] == NULL)
12276       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12277   }
12278
12279   user.nr = last_user_nr;
12280 }
12281
12282 void LoadSetupFromFilename(char *filename)
12283 {
12284   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12285
12286   if (setup_file_hash)
12287   {
12288     decodeSetupFileHash_Default(setup_file_hash);
12289
12290     freeSetupFileHash(setup_file_hash);
12291   }
12292   else
12293   {
12294     Debug("setup", "using default setup values");
12295   }
12296 }
12297
12298 static void LoadSetup_SpecialPostProcessing(void)
12299 {
12300   char *player_name_new;
12301
12302   // needed to work around problems with fixed length strings
12303   player_name_new = getFixedUserName(setup.player_name);
12304   free(setup.player_name);
12305   setup.player_name = player_name_new;
12306
12307   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12308   if (setup.scroll_delay == FALSE)
12309   {
12310     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12311     setup.scroll_delay = TRUE;                  // now always "on"
12312   }
12313
12314   // make sure that scroll delay value stays inside valid range
12315   setup.scroll_delay_value =
12316     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12317 }
12318
12319 void LoadSetup_Default(void)
12320 {
12321   char *filename;
12322
12323   // always start with reliable default values
12324   setSetupInfoToDefaults(&setup);
12325
12326   // try to load setup values from default setup file
12327   filename = getDefaultSetupFilename();
12328
12329   if (fileExists(filename))
12330     LoadSetupFromFilename(filename);
12331
12332   // try to load setup values from platform setup file
12333   filename = getPlatformSetupFilename();
12334
12335   if (fileExists(filename))
12336     LoadSetupFromFilename(filename);
12337
12338   // try to load setup values from user setup file
12339   filename = getSetupFilename();
12340
12341   LoadSetupFromFilename(filename);
12342
12343   LoadSetup_SpecialPostProcessing();
12344 }
12345
12346 void LoadSetup_AutoSetup(void)
12347 {
12348   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12349   SetupFileHash *setup_file_hash = NULL;
12350
12351   // always start with reliable default values
12352   setSetupInfoToDefaults_AutoSetup(&setup);
12353
12354   setup_file_hash = loadSetupFileHash(filename);
12355
12356   if (setup_file_hash)
12357   {
12358     decodeSetupFileHash_AutoSetup(setup_file_hash);
12359
12360     freeSetupFileHash(setup_file_hash);
12361   }
12362
12363   free(filename);
12364 }
12365
12366 void LoadSetup_ServerSetup(void)
12367 {
12368   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12369   SetupFileHash *setup_file_hash = NULL;
12370
12371   // always start with reliable default values
12372   setSetupInfoToDefaults_ServerSetup(&setup);
12373
12374   setup_file_hash = loadSetupFileHash(filename);
12375
12376   if (setup_file_hash)
12377   {
12378     decodeSetupFileHash_ServerSetup(setup_file_hash);
12379
12380     freeSetupFileHash(setup_file_hash);
12381   }
12382
12383   free(filename);
12384
12385   if (setup.player_uuid == NULL)
12386   {
12387     // player UUID does not yet exist in setup file
12388     setup.player_uuid = getStringCopy(getUUID());
12389     setup.player_version = 2;
12390
12391     SaveSetup_ServerSetup();
12392   }
12393 }
12394
12395 void LoadSetup_EditorCascade(void)
12396 {
12397   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12398   SetupFileHash *setup_file_hash = NULL;
12399
12400   // always start with reliable default values
12401   setSetupInfoToDefaults_EditorCascade(&setup);
12402
12403   setup_file_hash = loadSetupFileHash(filename);
12404
12405   if (setup_file_hash)
12406   {
12407     decodeSetupFileHash_EditorCascade(setup_file_hash);
12408
12409     freeSetupFileHash(setup_file_hash);
12410   }
12411
12412   free(filename);
12413 }
12414
12415 void LoadSetup(void)
12416 {
12417   LoadSetup_Default();
12418   LoadSetup_AutoSetup();
12419   LoadSetup_ServerSetup();
12420   LoadSetup_EditorCascade();
12421 }
12422
12423 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12424                                            char *mapping_line)
12425 {
12426   char mapping_guid[MAX_LINE_LEN];
12427   char *mapping_start, *mapping_end;
12428
12429   // get GUID from game controller mapping line: copy complete line
12430   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12431   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12432
12433   // get GUID from game controller mapping line: cut after GUID part
12434   mapping_start = strchr(mapping_guid, ',');
12435   if (mapping_start != NULL)
12436     *mapping_start = '\0';
12437
12438   // cut newline from game controller mapping line
12439   mapping_end = strchr(mapping_line, '\n');
12440   if (mapping_end != NULL)
12441     *mapping_end = '\0';
12442
12443   // add mapping entry to game controller mappings hash
12444   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12445 }
12446
12447 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12448                                                  char *filename)
12449 {
12450   FILE *file;
12451
12452   if (!(file = fopen(filename, MODE_READ)))
12453   {
12454     Warn("cannot read game controller mappings file '%s'", filename);
12455
12456     return;
12457   }
12458
12459   while (!feof(file))
12460   {
12461     char line[MAX_LINE_LEN];
12462
12463     if (!fgets(line, MAX_LINE_LEN, file))
12464       break;
12465
12466     addGameControllerMappingToHash(mappings_hash, line);
12467   }
12468
12469   fclose(file);
12470 }
12471
12472 void SaveSetup_Default(void)
12473 {
12474   char *filename = getSetupFilename();
12475   FILE *file;
12476   int i, pnr;
12477
12478   InitUserDataDirectory();
12479
12480   if (!(file = fopen(filename, MODE_WRITE)))
12481   {
12482     Warn("cannot write setup file '%s'", filename);
12483
12484     return;
12485   }
12486
12487   fprintFileHeader(file, SETUP_FILENAME);
12488
12489   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12490   {
12491     // just to make things nicer :)
12492     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12493         global_setup_tokens[i].value == &setup.sound                    ||
12494         global_setup_tokens[i].value == &setup.graphics_set             ||
12495         global_setup_tokens[i].value == &setup.volume_simple            ||
12496         global_setup_tokens[i].value == &setup.network_mode             ||
12497         global_setup_tokens[i].value == &setup.touch.control_type       ||
12498         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12499         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12500       fprintf(file, "\n");
12501
12502     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12503   }
12504
12505   for (i = 0; i < 2; i++)
12506   {
12507     int grid_xsize = setup.touch.grid_xsize[i];
12508     int grid_ysize = setup.touch.grid_ysize[i];
12509     int x, y;
12510
12511     fprintf(file, "\n");
12512
12513     for (y = 0; y < grid_ysize; y++)
12514     {
12515       char token_string[MAX_LINE_LEN];
12516       char value_string[MAX_LINE_LEN];
12517
12518       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12519
12520       for (x = 0; x < grid_xsize; x++)
12521       {
12522         char c = setup.touch.grid_button[i][x][y];
12523
12524         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12525       }
12526
12527       value_string[grid_xsize] = '\0';
12528
12529       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12530     }
12531   }
12532
12533   fprintf(file, "\n");
12534   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12535     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12536
12537   fprintf(file, "\n");
12538   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12539     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12540
12541   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12542   {
12543     char prefix[30];
12544
12545     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12546     fprintf(file, "\n");
12547
12548     setup_input = setup.input[pnr];
12549     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12550       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12551   }
12552
12553   fprintf(file, "\n");
12554   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12555     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12556
12557   // (internal setup values not saved to user setup file)
12558
12559   fprintf(file, "\n");
12560   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12561     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12562         setup.debug.xsn_mode != STATE_AUTO)
12563       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12564
12565   fprintf(file, "\n");
12566   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12567     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12568
12569   fclose(file);
12570
12571   SetFilePermissions(filename, PERMS_PRIVATE);
12572 }
12573
12574 void SaveSetup_AutoSetup(void)
12575 {
12576   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12577   FILE *file;
12578   int i;
12579
12580   InitUserDataDirectory();
12581
12582   if (!(file = fopen(filename, MODE_WRITE)))
12583   {
12584     Warn("cannot write auto setup file '%s'", filename);
12585
12586     free(filename);
12587
12588     return;
12589   }
12590
12591   fprintFileHeader(file, AUTOSETUP_FILENAME);
12592
12593   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12594     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12595
12596   fclose(file);
12597
12598   SetFilePermissions(filename, PERMS_PRIVATE);
12599
12600   free(filename);
12601 }
12602
12603 void SaveSetup_ServerSetup(void)
12604 {
12605   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12606   FILE *file;
12607   int i;
12608
12609   InitUserDataDirectory();
12610
12611   if (!(file = fopen(filename, MODE_WRITE)))
12612   {
12613     Warn("cannot write server setup file '%s'", filename);
12614
12615     free(filename);
12616
12617     return;
12618   }
12619
12620   fprintFileHeader(file, SERVERSETUP_FILENAME);
12621
12622   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12623   {
12624     // just to make things nicer :)
12625     if (server_setup_tokens[i].value == &setup.use_api_server)
12626       fprintf(file, "\n");
12627
12628     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12629   }
12630
12631   fclose(file);
12632
12633   SetFilePermissions(filename, PERMS_PRIVATE);
12634
12635   free(filename);
12636 }
12637
12638 void SaveSetup_EditorCascade(void)
12639 {
12640   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12641   FILE *file;
12642   int i;
12643
12644   InitUserDataDirectory();
12645
12646   if (!(file = fopen(filename, MODE_WRITE)))
12647   {
12648     Warn("cannot write editor cascade state file '%s'", filename);
12649
12650     free(filename);
12651
12652     return;
12653   }
12654
12655   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12656
12657   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12658     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12659
12660   fclose(file);
12661
12662   SetFilePermissions(filename, PERMS_PRIVATE);
12663
12664   free(filename);
12665 }
12666
12667 void SaveSetup(void)
12668 {
12669   SaveSetup_Default();
12670   SaveSetup_AutoSetup();
12671   SaveSetup_ServerSetup();
12672   SaveSetup_EditorCascade();
12673 }
12674
12675 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12676                                                   char *filename)
12677 {
12678   FILE *file;
12679
12680   if (!(file = fopen(filename, MODE_WRITE)))
12681   {
12682     Warn("cannot write game controller mappings file '%s'", filename);
12683
12684     return;
12685   }
12686
12687   BEGIN_HASH_ITERATION(mappings_hash, itr)
12688   {
12689     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12690   }
12691   END_HASH_ITERATION(mappings_hash, itr)
12692
12693   fclose(file);
12694 }
12695
12696 void SaveSetup_AddGameControllerMapping(char *mapping)
12697 {
12698   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12699   SetupFileHash *mappings_hash = newSetupFileHash();
12700
12701   InitUserDataDirectory();
12702
12703   // load existing personal game controller mappings
12704   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12705
12706   // add new mapping to personal game controller mappings
12707   addGameControllerMappingToHash(mappings_hash, mapping);
12708
12709   // save updated personal game controller mappings
12710   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12711
12712   freeSetupFileHash(mappings_hash);
12713   free(filename);
12714 }
12715
12716 void LoadCustomElementDescriptions(void)
12717 {
12718   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12719   SetupFileHash *setup_file_hash;
12720   int i;
12721
12722   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12723   {
12724     if (element_info[i].custom_description != NULL)
12725     {
12726       free(element_info[i].custom_description);
12727       element_info[i].custom_description = NULL;
12728     }
12729   }
12730
12731   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12732     return;
12733
12734   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12735   {
12736     char *token = getStringCat2(element_info[i].token_name, ".name");
12737     char *value = getHashEntry(setup_file_hash, token);
12738
12739     if (value != NULL)
12740       element_info[i].custom_description = getStringCopy(value);
12741
12742     free(token);
12743   }
12744
12745   freeSetupFileHash(setup_file_hash);
12746 }
12747
12748 static int getElementFromToken(char *token)
12749 {
12750   char *value = getHashEntry(element_token_hash, token);
12751
12752   if (value != NULL)
12753     return atoi(value);
12754
12755   Warn("unknown element token '%s'", token);
12756
12757   return EL_UNDEFINED;
12758 }
12759
12760 void FreeGlobalAnimEventInfo(void)
12761 {
12762   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12763
12764   if (gaei->event_list == NULL)
12765     return;
12766
12767   int i;
12768
12769   for (i = 0; i < gaei->num_event_lists; i++)
12770   {
12771     checked_free(gaei->event_list[i]->event_value);
12772     checked_free(gaei->event_list[i]);
12773   }
12774
12775   checked_free(gaei->event_list);
12776
12777   gaei->event_list = NULL;
12778   gaei->num_event_lists = 0;
12779 }
12780
12781 static int AddGlobalAnimEventList(void)
12782 {
12783   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12784   int list_pos = gaei->num_event_lists++;
12785
12786   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12787                                      sizeof(struct GlobalAnimEventListInfo *));
12788
12789   gaei->event_list[list_pos] =
12790     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12791
12792   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12793
12794   gaeli->event_value = NULL;
12795   gaeli->num_event_values = 0;
12796
12797   return list_pos;
12798 }
12799
12800 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12801 {
12802   // do not add empty global animation events
12803   if (event_value == ANIM_EVENT_NONE)
12804     return list_pos;
12805
12806   // if list position is undefined, create new list
12807   if (list_pos == ANIM_EVENT_UNDEFINED)
12808     list_pos = AddGlobalAnimEventList();
12809
12810   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12811   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12812   int value_pos = gaeli->num_event_values++;
12813
12814   gaeli->event_value = checked_realloc(gaeli->event_value,
12815                                        gaeli->num_event_values * sizeof(int *));
12816
12817   gaeli->event_value[value_pos] = event_value;
12818
12819   return list_pos;
12820 }
12821
12822 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12823 {
12824   if (list_pos == ANIM_EVENT_UNDEFINED)
12825     return ANIM_EVENT_NONE;
12826
12827   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12828   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12829
12830   return gaeli->event_value[value_pos];
12831 }
12832
12833 int GetGlobalAnimEventValueCount(int list_pos)
12834 {
12835   if (list_pos == ANIM_EVENT_UNDEFINED)
12836     return 0;
12837
12838   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12839   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12840
12841   return gaeli->num_event_values;
12842 }
12843
12844 // This function checks if a string <s> of the format "string1, string2, ..."
12845 // exactly contains a string <s_contained>.
12846
12847 static boolean string_has_parameter(char *s, char *s_contained)
12848 {
12849   char *substring;
12850
12851   if (s == NULL || s_contained == NULL)
12852     return FALSE;
12853
12854   if (strlen(s_contained) > strlen(s))
12855     return FALSE;
12856
12857   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12858   {
12859     char next_char = s[strlen(s_contained)];
12860
12861     // check if next character is delimiter or whitespace
12862     if (next_char == ',' || next_char == '\0' ||
12863         next_char == ' ' || next_char == '\t')
12864       return TRUE;
12865   }
12866
12867   // check if string contains another parameter string after a comma
12868   substring = strchr(s, ',');
12869   if (substring == NULL)        // string does not contain a comma
12870     return FALSE;
12871
12872   // advance string pointer to next character after the comma
12873   substring++;
12874
12875   // skip potential whitespaces after the comma
12876   while (*substring == ' ' || *substring == '\t')
12877     substring++;
12878
12879   return string_has_parameter(substring, s_contained);
12880 }
12881
12882 static int get_anim_parameter_value_ce(char *s)
12883 {
12884   char *s_ptr = s;
12885   char *pattern_1 = "ce_change:custom_";
12886   char *pattern_2 = ".page_";
12887   int pattern_1_len = strlen(pattern_1);
12888   char *matching_char = strstr(s_ptr, pattern_1);
12889   int result = ANIM_EVENT_NONE;
12890
12891   if (matching_char == NULL)
12892     return ANIM_EVENT_NONE;
12893
12894   result = ANIM_EVENT_CE_CHANGE;
12895
12896   s_ptr = matching_char + pattern_1_len;
12897
12898   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12899   if (*s_ptr >= '0' && *s_ptr <= '9')
12900   {
12901     int gic_ce_nr = (*s_ptr++ - '0');
12902
12903     if (*s_ptr >= '0' && *s_ptr <= '9')
12904     {
12905       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12906
12907       if (*s_ptr >= '0' && *s_ptr <= '9')
12908         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12909     }
12910
12911     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12912       return ANIM_EVENT_NONE;
12913
12914     // custom element stored as 0 to 255
12915     gic_ce_nr--;
12916
12917     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12918   }
12919   else
12920   {
12921     // invalid custom element number specified
12922
12923     return ANIM_EVENT_NONE;
12924   }
12925
12926   // check for change page number ("page_X" or "page_XX") (optional)
12927   if (strPrefix(s_ptr, pattern_2))
12928   {
12929     s_ptr += strlen(pattern_2);
12930
12931     if (*s_ptr >= '0' && *s_ptr <= '9')
12932     {
12933       int gic_page_nr = (*s_ptr++ - '0');
12934
12935       if (*s_ptr >= '0' && *s_ptr <= '9')
12936         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12937
12938       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12939         return ANIM_EVENT_NONE;
12940
12941       // change page stored as 1 to 32 (0 means "all change pages")
12942
12943       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12944     }
12945     else
12946     {
12947       // invalid animation part number specified
12948
12949       return ANIM_EVENT_NONE;
12950     }
12951   }
12952
12953   // discard result if next character is neither delimiter nor whitespace
12954   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12955         *s_ptr == ' ' || *s_ptr == '\t'))
12956     return ANIM_EVENT_NONE;
12957
12958   return result;
12959 }
12960
12961 static int get_anim_parameter_value(char *s)
12962 {
12963   int event_value[] =
12964   {
12965     ANIM_EVENT_CLICK,
12966     ANIM_EVENT_INIT,
12967     ANIM_EVENT_START,
12968     ANIM_EVENT_END,
12969     ANIM_EVENT_POST
12970   };
12971   char *pattern_1[] =
12972   {
12973     "click:anim_",
12974     "init:anim_",
12975     "start:anim_",
12976     "end:anim_",
12977     "post:anim_"
12978   };
12979   char *pattern_2 = ".part_";
12980   char *matching_char = NULL;
12981   char *s_ptr = s;
12982   int pattern_1_len = 0;
12983   int result = ANIM_EVENT_NONE;
12984   int i;
12985
12986   result = get_anim_parameter_value_ce(s);
12987
12988   if (result != ANIM_EVENT_NONE)
12989     return result;
12990
12991   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12992   {
12993     matching_char = strstr(s_ptr, pattern_1[i]);
12994     pattern_1_len = strlen(pattern_1[i]);
12995     result = event_value[i];
12996
12997     if (matching_char != NULL)
12998       break;
12999   }
13000
13001   if (matching_char == NULL)
13002     return ANIM_EVENT_NONE;
13003
13004   s_ptr = matching_char + pattern_1_len;
13005
13006   // check for main animation number ("anim_X" or "anim_XX")
13007   if (*s_ptr >= '0' && *s_ptr <= '9')
13008   {
13009     int gic_anim_nr = (*s_ptr++ - '0');
13010
13011     if (*s_ptr >= '0' && *s_ptr <= '9')
13012       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
13013
13014     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
13015       return ANIM_EVENT_NONE;
13016
13017     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
13018   }
13019   else
13020   {
13021     // invalid main animation number specified
13022
13023     return ANIM_EVENT_NONE;
13024   }
13025
13026   // check for animation part number ("part_X" or "part_XX") (optional)
13027   if (strPrefix(s_ptr, pattern_2))
13028   {
13029     s_ptr += strlen(pattern_2);
13030
13031     if (*s_ptr >= '0' && *s_ptr <= '9')
13032     {
13033       int gic_part_nr = (*s_ptr++ - '0');
13034
13035       if (*s_ptr >= '0' && *s_ptr <= '9')
13036         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
13037
13038       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
13039         return ANIM_EVENT_NONE;
13040
13041       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
13042     }
13043     else
13044     {
13045       // invalid animation part number specified
13046
13047       return ANIM_EVENT_NONE;
13048     }
13049   }
13050
13051   // discard result if next character is neither delimiter nor whitespace
13052   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
13053         *s_ptr == ' ' || *s_ptr == '\t'))
13054     return ANIM_EVENT_NONE;
13055
13056   return result;
13057 }
13058
13059 static int get_anim_parameter_values(char *s)
13060 {
13061   int list_pos = ANIM_EVENT_UNDEFINED;
13062   int event_value = ANIM_EVENT_DEFAULT;
13063
13064   if (string_has_parameter(s, "any"))
13065     event_value |= ANIM_EVENT_ANY;
13066
13067   if (string_has_parameter(s, "click:self") ||
13068       string_has_parameter(s, "click") ||
13069       string_has_parameter(s, "self"))
13070     event_value |= ANIM_EVENT_SELF;
13071
13072   if (string_has_parameter(s, "unclick:any"))
13073     event_value |= ANIM_EVENT_UNCLICK_ANY;
13074
13075   // if animation event found, add it to global animation event list
13076   if (event_value != ANIM_EVENT_NONE)
13077     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13078
13079   while (s != NULL)
13080   {
13081     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
13082     event_value = get_anim_parameter_value(s);
13083
13084     // if animation event found, add it to global animation event list
13085     if (event_value != ANIM_EVENT_NONE)
13086       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
13087
13088     // continue with next part of the string, starting with next comma
13089     s = strchr(s + 1, ',');
13090   }
13091
13092   return list_pos;
13093 }
13094
13095 static int get_anim_action_parameter_value(char *token)
13096 {
13097   // check most common default case first to massively speed things up
13098   if (strEqual(token, ARG_UNDEFINED))
13099     return ANIM_EVENT_ACTION_NONE;
13100
13101   int result = getImageIDFromToken(token);
13102
13103   if (result == -1)
13104   {
13105     char *gfx_token = getStringCat2("gfx.", token);
13106
13107     result = getImageIDFromToken(gfx_token);
13108
13109     checked_free(gfx_token);
13110   }
13111
13112   if (result == -1)
13113   {
13114     Key key = getKeyFromX11KeyName(token);
13115
13116     if (key != KSYM_UNDEFINED)
13117       result = -(int)key;
13118   }
13119
13120   if (result == -1)
13121   {
13122     if (isURL(token))
13123     {
13124       result = get_hash_from_string(token);     // unsigned int => int
13125       result = ABS(result);                     // may be negative now
13126       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
13127
13128       setHashEntry(anim_url_hash, int2str(result, 0), token);
13129     }
13130   }
13131
13132   if (result == -1)
13133     result = ANIM_EVENT_ACTION_NONE;
13134
13135   return result;
13136 }
13137
13138 int get_parameter_value(char *value_raw, char *suffix, int type)
13139 {
13140   char *value = getStringToLower(value_raw);
13141   int result = 0;       // probably a save default value
13142
13143   if (strEqual(suffix, ".direction"))
13144   {
13145     result = (strEqual(value, "left")  ? MV_LEFT :
13146               strEqual(value, "right") ? MV_RIGHT :
13147               strEqual(value, "up")    ? MV_UP :
13148               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
13149   }
13150   else if (strEqual(suffix, ".position"))
13151   {
13152     result = (strEqual(value, "left")   ? POS_LEFT :
13153               strEqual(value, "right")  ? POS_RIGHT :
13154               strEqual(value, "top")    ? POS_TOP :
13155               strEqual(value, "upper")  ? POS_UPPER :
13156               strEqual(value, "middle") ? POS_MIDDLE :
13157               strEqual(value, "lower")  ? POS_LOWER :
13158               strEqual(value, "bottom") ? POS_BOTTOM :
13159               strEqual(value, "any")    ? POS_ANY :
13160               strEqual(value, "ce")     ? POS_CE :
13161               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
13162               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
13163   }
13164   else if (strEqual(suffix, ".align"))
13165   {
13166     result = (strEqual(value, "left")   ? ALIGN_LEFT :
13167               strEqual(value, "right")  ? ALIGN_RIGHT :
13168               strEqual(value, "center") ? ALIGN_CENTER :
13169               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
13170   }
13171   else if (strEqual(suffix, ".valign"))
13172   {
13173     result = (strEqual(value, "top")    ? VALIGN_TOP :
13174               strEqual(value, "bottom") ? VALIGN_BOTTOM :
13175               strEqual(value, "middle") ? VALIGN_MIDDLE :
13176               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
13177   }
13178   else if (strEqual(suffix, ".anim_mode"))
13179   {
13180     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13181               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13182               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13183               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13184               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13185               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13186               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13187               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13188               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13189               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13190               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13191               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13192               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13193               string_has_parameter(value, "all")        ? ANIM_ALL :
13194               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13195               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13196               ANIM_DEFAULT);
13197
13198     if (string_has_parameter(value, "once"))
13199       result |= ANIM_ONCE;
13200
13201     if (string_has_parameter(value, "reverse"))
13202       result |= ANIM_REVERSE;
13203
13204     if (string_has_parameter(value, "opaque_player"))
13205       result |= ANIM_OPAQUE_PLAYER;
13206
13207     if (string_has_parameter(value, "static_panel"))
13208       result |= ANIM_STATIC_PANEL;
13209   }
13210   else if (strEqual(suffix, ".init_event") ||
13211            strEqual(suffix, ".anim_event"))
13212   {
13213     result = get_anim_parameter_values(value);
13214   }
13215   else if (strEqual(suffix, ".init_delay_action") ||
13216            strEqual(suffix, ".anim_delay_action") ||
13217            strEqual(suffix, ".post_delay_action") ||
13218            strEqual(suffix, ".init_event_action") ||
13219            strEqual(suffix, ".anim_event_action"))
13220   {
13221     result = get_anim_action_parameter_value(value_raw);
13222   }
13223   else if (strEqual(suffix, ".class"))
13224   {
13225     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13226               get_hash_from_string(value));
13227   }
13228   else if (strEqual(suffix, ".style"))
13229   {
13230     result = STYLE_DEFAULT;
13231
13232     if (string_has_parameter(value, "accurate_borders"))
13233       result |= STYLE_ACCURATE_BORDERS;
13234
13235     if (string_has_parameter(value, "inner_corners"))
13236       result |= STYLE_INNER_CORNERS;
13237
13238     if (string_has_parameter(value, "reverse"))
13239       result |= STYLE_REVERSE;
13240
13241     if (string_has_parameter(value, "leftmost_position"))
13242       result |= STYLE_LEFTMOST_POSITION;
13243
13244     if (string_has_parameter(value, "block_clicks"))
13245       result |= STYLE_BLOCK;
13246
13247     if (string_has_parameter(value, "passthrough_clicks"))
13248       result |= STYLE_PASSTHROUGH;
13249
13250     if (string_has_parameter(value, "multiple_actions"))
13251       result |= STYLE_MULTIPLE_ACTIONS;
13252
13253     if (string_has_parameter(value, "consume_ce_event"))
13254       result |= STYLE_CONSUME_CE_EVENT;
13255   }
13256   else if (strEqual(suffix, ".fade_mode"))
13257   {
13258     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13259               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13260               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13261               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13262               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13263               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13264               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13265               FADE_MODE_DEFAULT);
13266   }
13267   else if (strEqual(suffix, ".auto_delay_unit"))
13268   {
13269     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13270               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13271               AUTO_DELAY_UNIT_DEFAULT);
13272   }
13273   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13274   {
13275     result = gfx.get_font_from_token_function(value);
13276   }
13277   else          // generic parameter of type integer or boolean
13278   {
13279     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13280               type == TYPE_INTEGER ? get_integer_from_string(value) :
13281               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13282               ARG_UNDEFINED_VALUE);
13283   }
13284
13285   free(value);
13286
13287   return result;
13288 }
13289
13290 static int get_token_parameter_value(char *token, char *value_raw)
13291 {
13292   char *suffix;
13293
13294   if (token == NULL || value_raw == NULL)
13295     return ARG_UNDEFINED_VALUE;
13296
13297   suffix = strrchr(token, '.');
13298   if (suffix == NULL)
13299     suffix = token;
13300
13301   if (strEqual(suffix, ".element"))
13302     return getElementFromToken(value_raw);
13303
13304   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13305   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13306 }
13307
13308 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13309                                      boolean ignore_defaults)
13310 {
13311   int i;
13312
13313   for (i = 0; image_config_vars[i].token != NULL; i++)
13314   {
13315     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13316
13317     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13318     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13319       continue;
13320
13321     if (value != NULL)
13322       *image_config_vars[i].value =
13323         get_token_parameter_value(image_config_vars[i].token, value);
13324   }
13325 }
13326
13327 void InitMenuDesignSettings_Static(void)
13328 {
13329   // always start with reliable default values from static default config
13330   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13331 }
13332
13333 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13334 {
13335   int i;
13336
13337   // the following initializes hierarchical values from static configuration
13338
13339   // special case: initialize "ARG_DEFAULT" values in static default config
13340   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13341   titlescreen_initial_first_default.fade_mode  =
13342     title_initial_first_default.fade_mode;
13343   titlescreen_initial_first_default.fade_delay =
13344     title_initial_first_default.fade_delay;
13345   titlescreen_initial_first_default.post_delay =
13346     title_initial_first_default.post_delay;
13347   titlescreen_initial_first_default.auto_delay =
13348     title_initial_first_default.auto_delay;
13349   titlescreen_initial_first_default.auto_delay_unit =
13350     title_initial_first_default.auto_delay_unit;
13351   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13352   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13353   titlescreen_first_default.post_delay = title_first_default.post_delay;
13354   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13355   titlescreen_first_default.auto_delay_unit =
13356     title_first_default.auto_delay_unit;
13357   titlemessage_initial_first_default.fade_mode  =
13358     title_initial_first_default.fade_mode;
13359   titlemessage_initial_first_default.fade_delay =
13360     title_initial_first_default.fade_delay;
13361   titlemessage_initial_first_default.post_delay =
13362     title_initial_first_default.post_delay;
13363   titlemessage_initial_first_default.auto_delay =
13364     title_initial_first_default.auto_delay;
13365   titlemessage_initial_first_default.auto_delay_unit =
13366     title_initial_first_default.auto_delay_unit;
13367   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13368   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13369   titlemessage_first_default.post_delay = title_first_default.post_delay;
13370   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13371   titlemessage_first_default.auto_delay_unit =
13372     title_first_default.auto_delay_unit;
13373
13374   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13375   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13376   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13377   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13378   titlescreen_initial_default.auto_delay_unit =
13379     title_initial_default.auto_delay_unit;
13380   titlescreen_default.fade_mode  = title_default.fade_mode;
13381   titlescreen_default.fade_delay = title_default.fade_delay;
13382   titlescreen_default.post_delay = title_default.post_delay;
13383   titlescreen_default.auto_delay = title_default.auto_delay;
13384   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13385   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13386   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13387   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13388   titlemessage_initial_default.auto_delay_unit =
13389     title_initial_default.auto_delay_unit;
13390   titlemessage_default.fade_mode  = title_default.fade_mode;
13391   titlemessage_default.fade_delay = title_default.fade_delay;
13392   titlemessage_default.post_delay = title_default.post_delay;
13393   titlemessage_default.auto_delay = title_default.auto_delay;
13394   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13395
13396   // special case: initialize "ARG_DEFAULT" values in static default config
13397   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13398   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13399   {
13400     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13401     titlescreen_first[i] = titlescreen_first_default;
13402     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13403     titlemessage_first[i] = titlemessage_first_default;
13404
13405     titlescreen_initial[i] = titlescreen_initial_default;
13406     titlescreen[i] = titlescreen_default;
13407     titlemessage_initial[i] = titlemessage_initial_default;
13408     titlemessage[i] = titlemessage_default;
13409   }
13410
13411   // special case: initialize "ARG_DEFAULT" values in static default config
13412   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13413   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13414   {
13415     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13416       continue;
13417
13418     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13419     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13420     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13421   }
13422
13423   // special case: initialize "ARG_DEFAULT" values in static default config
13424   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13425   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13426   {
13427     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13428     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13429     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13430
13431     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13432       continue;
13433
13434     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13435   }
13436 }
13437
13438 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13439 {
13440   static struct
13441   {
13442     struct XY *dst, *src;
13443   }
13444   game_buttons_xy[] =
13445   {
13446     { &game.button.save,        &game.button.stop       },
13447     { &game.button.pause2,      &game.button.pause      },
13448     { &game.button.load,        &game.button.play       },
13449     { &game.button.undo,        &game.button.stop       },
13450     { &game.button.redo,        &game.button.play       },
13451
13452     { NULL,                     NULL                    }
13453   };
13454   int i, j;
13455
13456   // special case: initialize later added SETUP list size from LEVELS value
13457   if (menu.list_size[GAME_MODE_SETUP] == -1)
13458     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13459
13460   // set default position for snapshot buttons to stop/pause/play buttons
13461   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13462     if ((*game_buttons_xy[i].dst).x == -1 &&
13463         (*game_buttons_xy[i].dst).y == -1)
13464       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13465
13466   // --------------------------------------------------------------------------
13467   // dynamic viewports (including playfield margins, borders and alignments)
13468   // --------------------------------------------------------------------------
13469
13470   // dynamic viewports currently only supported for landscape mode
13471   int display_width  = MAX(video.display_width, video.display_height);
13472   int display_height = MIN(video.display_width, video.display_height);
13473
13474   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13475   {
13476     struct RectWithBorder *vp_window    = &viewport.window[i];
13477     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13478     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13479     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13480     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13481     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13482     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13483     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13484
13485     // adjust window size if min/max width/height is specified
13486
13487     if (vp_window->min_width != -1)
13488     {
13489       int window_width = display_width;
13490
13491       // when using static window height, use aspect ratio of display
13492       if (vp_window->min_height == -1)
13493         window_width = vp_window->height * display_width / display_height;
13494
13495       vp_window->width = MAX(vp_window->min_width, window_width);
13496     }
13497
13498     if (vp_window->min_height != -1)
13499     {
13500       int window_height = display_height;
13501
13502       // when using static window width, use aspect ratio of display
13503       if (vp_window->min_width == -1)
13504         window_height = vp_window->width * display_height / display_width;
13505
13506       vp_window->height = MAX(vp_window->min_height, window_height);
13507     }
13508
13509     if (vp_window->max_width != -1)
13510       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13511
13512     if (vp_window->max_height != -1)
13513       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13514
13515     int playfield_width  = vp_window->width;
13516     int playfield_height = vp_window->height;
13517
13518     // adjust playfield size and position according to specified margins
13519
13520     playfield_width  -= vp_playfield->margin_left;
13521     playfield_width  -= vp_playfield->margin_right;
13522
13523     playfield_height -= vp_playfield->margin_top;
13524     playfield_height -= vp_playfield->margin_bottom;
13525
13526     // adjust playfield size if min/max width/height is specified
13527
13528     if (vp_playfield->min_width != -1)
13529       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13530
13531     if (vp_playfield->min_height != -1)
13532       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13533
13534     if (vp_playfield->max_width != -1)
13535       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13536
13537     if (vp_playfield->max_height != -1)
13538       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13539
13540     // adjust playfield position according to specified alignment
13541
13542     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13543       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13544     else if (vp_playfield->align == ALIGN_CENTER)
13545       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13546     else if (vp_playfield->align == ALIGN_RIGHT)
13547       vp_playfield->x += playfield_width - vp_playfield->width;
13548
13549     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13550       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13551     else if (vp_playfield->valign == VALIGN_MIDDLE)
13552       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13553     else if (vp_playfield->valign == VALIGN_BOTTOM)
13554       vp_playfield->y += playfield_height - vp_playfield->height;
13555
13556     vp_playfield->x += vp_playfield->margin_left;
13557     vp_playfield->y += vp_playfield->margin_top;
13558
13559     // adjust individual playfield borders if only default border is specified
13560
13561     if (vp_playfield->border_left == -1)
13562       vp_playfield->border_left = vp_playfield->border_size;
13563     if (vp_playfield->border_right == -1)
13564       vp_playfield->border_right = vp_playfield->border_size;
13565     if (vp_playfield->border_top == -1)
13566       vp_playfield->border_top = vp_playfield->border_size;
13567     if (vp_playfield->border_bottom == -1)
13568       vp_playfield->border_bottom = vp_playfield->border_size;
13569
13570     // set dynamic playfield borders if borders are specified as undefined
13571     // (but only if window size was dynamic and playfield size was static)
13572
13573     if (dynamic_window_width && !dynamic_playfield_width)
13574     {
13575       if (vp_playfield->border_left == -1)
13576       {
13577         vp_playfield->border_left = (vp_playfield->x -
13578                                      vp_playfield->margin_left);
13579         vp_playfield->x     -= vp_playfield->border_left;
13580         vp_playfield->width += vp_playfield->border_left;
13581       }
13582
13583       if (vp_playfield->border_right == -1)
13584       {
13585         vp_playfield->border_right = (vp_window->width -
13586                                       vp_playfield->x -
13587                                       vp_playfield->width -
13588                                       vp_playfield->margin_right);
13589         vp_playfield->width += vp_playfield->border_right;
13590       }
13591     }
13592
13593     if (dynamic_window_height && !dynamic_playfield_height)
13594     {
13595       if (vp_playfield->border_top == -1)
13596       {
13597         vp_playfield->border_top = (vp_playfield->y -
13598                                     vp_playfield->margin_top);
13599         vp_playfield->y      -= vp_playfield->border_top;
13600         vp_playfield->height += vp_playfield->border_top;
13601       }
13602
13603       if (vp_playfield->border_bottom == -1)
13604       {
13605         vp_playfield->border_bottom = (vp_window->height -
13606                                        vp_playfield->y -
13607                                        vp_playfield->height -
13608                                        vp_playfield->margin_bottom);
13609         vp_playfield->height += vp_playfield->border_bottom;
13610       }
13611     }
13612
13613     // adjust playfield size to be a multiple of a defined alignment tile size
13614
13615     int align_size = vp_playfield->align_size;
13616     int playfield_xtiles = vp_playfield->width  / align_size;
13617     int playfield_ytiles = vp_playfield->height / align_size;
13618     int playfield_width_corrected  = playfield_xtiles * align_size;
13619     int playfield_height_corrected = playfield_ytiles * align_size;
13620     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13621                                  i == GFX_SPECIAL_ARG_EDITOR);
13622
13623     if (is_playfield_mode &&
13624         dynamic_playfield_width &&
13625         vp_playfield->width != playfield_width_corrected)
13626     {
13627       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13628
13629       vp_playfield->width = playfield_width_corrected;
13630
13631       if (vp_playfield->align == ALIGN_LEFT)
13632       {
13633         vp_playfield->border_left += playfield_xdiff;
13634       }
13635       else if (vp_playfield->align == ALIGN_RIGHT)
13636       {
13637         vp_playfield->border_right += playfield_xdiff;
13638       }
13639       else if (vp_playfield->align == ALIGN_CENTER)
13640       {
13641         int border_left_diff  = playfield_xdiff / 2;
13642         int border_right_diff = playfield_xdiff - border_left_diff;
13643
13644         vp_playfield->border_left  += border_left_diff;
13645         vp_playfield->border_right += border_right_diff;
13646       }
13647     }
13648
13649     if (is_playfield_mode &&
13650         dynamic_playfield_height &&
13651         vp_playfield->height != playfield_height_corrected)
13652     {
13653       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13654
13655       vp_playfield->height = playfield_height_corrected;
13656
13657       if (vp_playfield->valign == VALIGN_TOP)
13658       {
13659         vp_playfield->border_top += playfield_ydiff;
13660       }
13661       else if (vp_playfield->align == VALIGN_BOTTOM)
13662       {
13663         vp_playfield->border_right += playfield_ydiff;
13664       }
13665       else if (vp_playfield->align == VALIGN_MIDDLE)
13666       {
13667         int border_top_diff    = playfield_ydiff / 2;
13668         int border_bottom_diff = playfield_ydiff - border_top_diff;
13669
13670         vp_playfield->border_top    += border_top_diff;
13671         vp_playfield->border_bottom += border_bottom_diff;
13672       }
13673     }
13674
13675     // adjust door positions according to specified alignment
13676
13677     for (j = 0; j < 2; j++)
13678     {
13679       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13680
13681       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13682         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13683       else if (vp_door->align == ALIGN_CENTER)
13684         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13685       else if (vp_door->align == ALIGN_RIGHT)
13686         vp_door->x += vp_window->width - vp_door->width;
13687
13688       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13689         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13690       else if (vp_door->valign == VALIGN_MIDDLE)
13691         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13692       else if (vp_door->valign == VALIGN_BOTTOM)
13693         vp_door->y += vp_window->height - vp_door->height;
13694     }
13695   }
13696 }
13697
13698 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13699 {
13700   static struct
13701   {
13702     struct XYTileSize *dst, *src;
13703     int graphic;
13704   }
13705   editor_buttons_xy[] =
13706   {
13707     {
13708       &editor.button.element_left,      &editor.palette.element_left,
13709       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13710     },
13711     {
13712       &editor.button.element_middle,    &editor.palette.element_middle,
13713       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13714     },
13715     {
13716       &editor.button.element_right,     &editor.palette.element_right,
13717       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13718     },
13719
13720     { NULL,                     NULL                    }
13721   };
13722   int i;
13723
13724   // set default position for element buttons to element graphics
13725   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13726   {
13727     if ((*editor_buttons_xy[i].dst).x == -1 &&
13728         (*editor_buttons_xy[i].dst).y == -1)
13729     {
13730       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13731
13732       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13733
13734       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13735     }
13736   }
13737
13738   // adjust editor palette rows and columns if specified to be dynamic
13739
13740   if (editor.palette.cols == -1)
13741   {
13742     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13743     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13744     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13745
13746     editor.palette.cols = (vp_width - sc_width) / bt_width;
13747
13748     if (editor.palette.x == -1)
13749     {
13750       int palette_width = editor.palette.cols * bt_width + sc_width;
13751
13752       editor.palette.x = (vp_width - palette_width) / 2;
13753     }
13754   }
13755
13756   if (editor.palette.rows == -1)
13757   {
13758     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13759     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13760     int tx_height = getFontHeight(FONT_TEXT_2);
13761
13762     editor.palette.rows = (vp_height - tx_height) / bt_height;
13763
13764     if (editor.palette.y == -1)
13765     {
13766       int palette_height = editor.palette.rows * bt_height + tx_height;
13767
13768       editor.palette.y = (vp_height - palette_height) / 2;
13769     }
13770   }
13771 }
13772
13773 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13774                                                       boolean initialize)
13775 {
13776   // special case: check if network and preview player positions are redefined,
13777   // to compare this later against the main menu level preview being redefined
13778   struct TokenIntPtrInfo menu_config_players[] =
13779   {
13780     { "main.network_players.x", &menu.main.network_players.redefined    },
13781     { "main.network_players.y", &menu.main.network_players.redefined    },
13782     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13783     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13784     { "preview.x",              &preview.redefined                      },
13785     { "preview.y",              &preview.redefined                      }
13786   };
13787   int i;
13788
13789   if (initialize)
13790   {
13791     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13792       *menu_config_players[i].value = FALSE;
13793   }
13794   else
13795   {
13796     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13797       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13798         *menu_config_players[i].value = TRUE;
13799   }
13800 }
13801
13802 static void InitMenuDesignSettings_PreviewPlayers(void)
13803 {
13804   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13805 }
13806
13807 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13808 {
13809   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13810 }
13811
13812 static void LoadMenuDesignSettingsFromFilename(char *filename)
13813 {
13814   static struct TitleFadingInfo tfi;
13815   static struct TitleMessageInfo tmi;
13816   static struct TokenInfo title_tokens[] =
13817   {
13818     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13819     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13820     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13821     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13822     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13823
13824     { -1,               NULL,                   NULL                    }
13825   };
13826   static struct TokenInfo titlemessage_tokens[] =
13827   {
13828     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13829     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13830     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13831     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13832     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13833     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13834     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13835     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13836     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13837     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13838     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13839     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13840     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13841     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13842     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13843     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13844     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13845     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13846
13847     { -1,               NULL,                   NULL                    }
13848   };
13849   static struct
13850   {
13851     struct TitleFadingInfo *info;
13852     char *text;
13853   }
13854   title_info[] =
13855   {
13856     // initialize first titles from "enter screen" definitions, if defined
13857     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13858     { &title_first_default,             "menu.enter_screen.TITLE"       },
13859
13860     // initialize title screens from "next screen" definitions, if defined
13861     { &title_initial_default,           "menu.next_screen.TITLE"        },
13862     { &title_default,                   "menu.next_screen.TITLE"        },
13863
13864     { NULL,                             NULL                            }
13865   };
13866   static struct
13867   {
13868     struct TitleMessageInfo *array;
13869     char *text;
13870   }
13871   titlemessage_arrays[] =
13872   {
13873     // initialize first titles from "enter screen" definitions, if defined
13874     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13875     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13876     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13877     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13878
13879     // initialize titles from "next screen" definitions, if defined
13880     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13881     { titlescreen,                      "menu.next_screen.TITLE"        },
13882     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13883     { titlemessage,                     "menu.next_screen.TITLE"        },
13884
13885     // overwrite titles with title definitions, if defined
13886     { titlescreen_initial_first,        "[title_initial]"               },
13887     { titlescreen_first,                "[title]"                       },
13888     { titlemessage_initial_first,       "[title_initial]"               },
13889     { titlemessage_first,               "[title]"                       },
13890
13891     { titlescreen_initial,              "[title_initial]"               },
13892     { titlescreen,                      "[title]"                       },
13893     { titlemessage_initial,             "[title_initial]"               },
13894     { titlemessage,                     "[title]"                       },
13895
13896     // overwrite titles with title screen/message definitions, if defined
13897     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13898     { titlescreen_first,                "[titlescreen]"                 },
13899     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13900     { titlemessage_first,               "[titlemessage]"                },
13901
13902     { titlescreen_initial,              "[titlescreen_initial]"         },
13903     { titlescreen,                      "[titlescreen]"                 },
13904     { titlemessage_initial,             "[titlemessage_initial]"        },
13905     { titlemessage,                     "[titlemessage]"                },
13906
13907     { NULL,                             NULL                            }
13908   };
13909   SetupFileHash *setup_file_hash;
13910   int i, j, k;
13911
13912   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13913     return;
13914
13915   // the following initializes hierarchical values from dynamic configuration
13916
13917   // special case: initialize with default values that may be overwritten
13918   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13919   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13920   {
13921     struct TokenIntPtrInfo menu_config[] =
13922     {
13923       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13924       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13925       { "menu.list_size",       &menu.list_size[i]      }
13926     };
13927
13928     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13929     {
13930       char *token = menu_config[j].token;
13931       char *value = getHashEntry(setup_file_hash, token);
13932
13933       if (value != NULL)
13934         *menu_config[j].value = get_integer_from_string(value);
13935     }
13936   }
13937
13938   // special case: initialize with default values that may be overwritten
13939   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13940   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13941   {
13942     struct TokenIntPtrInfo menu_config[] =
13943     {
13944       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13945       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13946       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13947       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13948       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13949     };
13950
13951     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13952     {
13953       char *token = menu_config[j].token;
13954       char *value = getHashEntry(setup_file_hash, token);
13955
13956       if (value != NULL)
13957         *menu_config[j].value = get_integer_from_string(value);
13958     }
13959   }
13960
13961   // special case: initialize with default values that may be overwritten
13962   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13963   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13964   {
13965     struct TokenIntPtrInfo menu_config[] =
13966     {
13967       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13968       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13969     };
13970
13971     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13972     {
13973       char *token = menu_config[j].token;
13974       char *value = getHashEntry(setup_file_hash, token);
13975
13976       if (value != NULL)
13977         *menu_config[j].value = get_integer_from_string(value);
13978     }
13979   }
13980
13981   // special case: initialize with default values that may be overwritten
13982   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13983   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13984   {
13985     struct TokenIntPtrInfo menu_config[] =
13986     {
13987       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13988       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13989       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13990       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13991       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13992       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13993       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13994       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13995       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13996       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13997     };
13998
13999     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14000     {
14001       char *token = menu_config[j].token;
14002       char *value = getHashEntry(setup_file_hash, token);
14003
14004       if (value != NULL)
14005         *menu_config[j].value = get_integer_from_string(value);
14006     }
14007   }
14008
14009   // special case: initialize with default values that may be overwritten
14010   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
14011   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14012   {
14013     struct TokenIntPtrInfo menu_config[] =
14014     {
14015       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
14016       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
14017       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
14018       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
14019       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
14020       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
14021       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
14022       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
14023       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
14024     };
14025
14026     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
14027     {
14028       char *token = menu_config[j].token;
14029       char *value = getHashEntry(setup_file_hash, token);
14030
14031       if (value != NULL)
14032         *menu_config[j].value = get_token_parameter_value(token, value);
14033     }
14034   }
14035
14036   // special case: initialize with default values that may be overwritten
14037   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
14038   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
14039   {
14040     struct
14041     {
14042       char *token_prefix;
14043       struct RectWithBorder *struct_ptr;
14044     }
14045     vp_struct[] =
14046     {
14047       { "viewport.window",      &viewport.window[i]     },
14048       { "viewport.playfield",   &viewport.playfield[i]  },
14049       { "viewport.door_1",      &viewport.door_1[i]     },
14050       { "viewport.door_2",      &viewport.door_2[i]     }
14051     };
14052
14053     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
14054     {
14055       struct TokenIntPtrInfo vp_config[] =
14056       {
14057         { ".x",                 &vp_struct[j].struct_ptr->x             },
14058         { ".y",                 &vp_struct[j].struct_ptr->y             },
14059         { ".width",             &vp_struct[j].struct_ptr->width         },
14060         { ".height",            &vp_struct[j].struct_ptr->height        },
14061         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
14062         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
14063         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
14064         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
14065         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
14066         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
14067         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
14068         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
14069         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
14070         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
14071         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
14072         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
14073         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
14074         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
14075         { ".align",             &vp_struct[j].struct_ptr->align         },
14076         { ".valign",            &vp_struct[j].struct_ptr->valign        }
14077       };
14078
14079       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
14080       {
14081         char *token = getStringCat2(vp_struct[j].token_prefix,
14082                                     vp_config[k].token);
14083         char *value = getHashEntry(setup_file_hash, token);
14084
14085         if (value != NULL)
14086           *vp_config[k].value = get_token_parameter_value(token, value);
14087
14088         free(token);
14089       }
14090     }
14091   }
14092
14093   // special case: initialize with default values that may be overwritten
14094   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
14095   for (i = 0; title_info[i].info != NULL; i++)
14096   {
14097     struct TitleFadingInfo *info = title_info[i].info;
14098     char *base_token = title_info[i].text;
14099
14100     for (j = 0; title_tokens[j].type != -1; j++)
14101     {
14102       char *token = getStringCat2(base_token, title_tokens[j].text);
14103       char *value = getHashEntry(setup_file_hash, token);
14104
14105       if (value != NULL)
14106       {
14107         int parameter_value = get_token_parameter_value(token, value);
14108
14109         tfi = *info;
14110
14111         *(int *)title_tokens[j].value = (int)parameter_value;
14112
14113         *info = tfi;
14114       }
14115
14116       free(token);
14117     }
14118   }
14119
14120   // special case: initialize with default values that may be overwritten
14121   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
14122   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
14123   {
14124     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
14125     char *base_token = titlemessage_arrays[i].text;
14126
14127     for (j = 0; titlemessage_tokens[j].type != -1; j++)
14128     {
14129       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
14130       char *value = getHashEntry(setup_file_hash, token);
14131
14132       if (value != NULL)
14133       {
14134         int parameter_value = get_token_parameter_value(token, value);
14135
14136         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
14137         {
14138           tmi = array[k];
14139
14140           if (titlemessage_tokens[j].type == TYPE_INTEGER)
14141             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
14142           else
14143             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
14144
14145           array[k] = tmi;
14146         }
14147       }
14148
14149       free(token);
14150     }
14151   }
14152
14153   // read (and overwrite with) values that may be specified in config file
14154   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
14155
14156   // special case: check if network and preview player positions are redefined
14157   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
14158
14159   freeSetupFileHash(setup_file_hash);
14160 }
14161
14162 void LoadMenuDesignSettings(void)
14163 {
14164   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14165
14166   InitMenuDesignSettings_Static();
14167   InitMenuDesignSettings_SpecialPreProcessing();
14168   InitMenuDesignSettings_PreviewPlayers();
14169
14170   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
14171   {
14172     // first look for special settings configured in level series config
14173     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
14174
14175     if (fileExists(filename_base))
14176       LoadMenuDesignSettingsFromFilename(filename_base);
14177   }
14178
14179   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14180
14181   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14182     LoadMenuDesignSettingsFromFilename(filename_local);
14183
14184   InitMenuDesignSettings_SpecialPostProcessing();
14185 }
14186
14187 void LoadMenuDesignSettings_AfterGraphics(void)
14188 {
14189   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14190 }
14191
14192 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14193                                 boolean ignore_defaults)
14194 {
14195   int i;
14196
14197   for (i = 0; sound_config_vars[i].token != NULL; i++)
14198   {
14199     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14200
14201     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14202     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14203       continue;
14204
14205     if (value != NULL)
14206       *sound_config_vars[i].value =
14207         get_token_parameter_value(sound_config_vars[i].token, value);
14208   }
14209 }
14210
14211 void InitSoundSettings_Static(void)
14212 {
14213   // always start with reliable default values from static default config
14214   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14215 }
14216
14217 static void LoadSoundSettingsFromFilename(char *filename)
14218 {
14219   SetupFileHash *setup_file_hash;
14220
14221   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14222     return;
14223
14224   // read (and overwrite with) values that may be specified in config file
14225   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14226
14227   freeSetupFileHash(setup_file_hash);
14228 }
14229
14230 void LoadSoundSettings(void)
14231 {
14232   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14233
14234   InitSoundSettings_Static();
14235
14236   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14237   {
14238     // first look for special settings configured in level series config
14239     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14240
14241     if (fileExists(filename_base))
14242       LoadSoundSettingsFromFilename(filename_base);
14243   }
14244
14245   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14246
14247   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14248     LoadSoundSettingsFromFilename(filename_local);
14249 }
14250
14251 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14252 {
14253   char *filename = getEditorSetupFilename();
14254   SetupFileList *setup_file_list, *list;
14255   SetupFileHash *element_hash;
14256   int num_unknown_tokens = 0;
14257   int i;
14258
14259   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14260     return;
14261
14262   element_hash = newSetupFileHash();
14263
14264   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14265     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14266
14267   // determined size may be larger than needed (due to unknown elements)
14268   *num_elements = 0;
14269   for (list = setup_file_list; list != NULL; list = list->next)
14270     (*num_elements)++;
14271
14272   // add space for up to 3 more elements for padding that may be needed
14273   *num_elements += 3;
14274
14275   // free memory for old list of elements, if needed
14276   checked_free(*elements);
14277
14278   // allocate memory for new list of elements
14279   *elements = checked_malloc(*num_elements * sizeof(int));
14280
14281   *num_elements = 0;
14282   for (list = setup_file_list; list != NULL; list = list->next)
14283   {
14284     char *value = getHashEntry(element_hash, list->token);
14285
14286     if (value == NULL)          // try to find obsolete token mapping
14287     {
14288       char *mapped_token = get_mapped_token(list->token);
14289
14290       if (mapped_token != NULL)
14291       {
14292         value = getHashEntry(element_hash, mapped_token);
14293
14294         free(mapped_token);
14295       }
14296     }
14297
14298     if (value != NULL)
14299     {
14300       (*elements)[(*num_elements)++] = atoi(value);
14301     }
14302     else
14303     {
14304       if (num_unknown_tokens == 0)
14305       {
14306         Warn("---");
14307         Warn("unknown token(s) found in config file:");
14308         Warn("- config file: '%s'", filename);
14309
14310         num_unknown_tokens++;
14311       }
14312
14313       Warn("- token: '%s'", list->token);
14314     }
14315   }
14316
14317   if (num_unknown_tokens > 0)
14318     Warn("---");
14319
14320   while (*num_elements % 4)     // pad with empty elements, if needed
14321     (*elements)[(*num_elements)++] = EL_EMPTY;
14322
14323   freeSetupFileList(setup_file_list);
14324   freeSetupFileHash(element_hash);
14325
14326 #if 0
14327   for (i = 0; i < *num_elements; i++)
14328     Debug("editor", "element '%s' [%d]\n",
14329           element_info[(*elements)[i]].token_name, (*elements)[i]);
14330 #endif
14331 }
14332
14333 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14334                                                      boolean is_sound)
14335 {
14336   SetupFileHash *setup_file_hash = NULL;
14337   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14338   char *filename_music, *filename_prefix, *filename_info;
14339   struct
14340   {
14341     char *token;
14342     char **value_ptr;
14343   }
14344   token_to_value_ptr[] =
14345   {
14346     { "title_header",   &tmp_music_file_info.title_header       },
14347     { "artist_header",  &tmp_music_file_info.artist_header      },
14348     { "album_header",   &tmp_music_file_info.album_header       },
14349     { "year_header",    &tmp_music_file_info.year_header        },
14350     { "played_header",  &tmp_music_file_info.played_header      },
14351
14352     { "title",          &tmp_music_file_info.title              },
14353     { "artist",         &tmp_music_file_info.artist             },
14354     { "album",          &tmp_music_file_info.album              },
14355     { "year",           &tmp_music_file_info.year               },
14356     { "played",         &tmp_music_file_info.played             },
14357
14358     { NULL,             NULL                                    },
14359   };
14360   int i;
14361
14362   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14363                     getCustomMusicFilename(basename));
14364
14365   if (filename_music == NULL)
14366     return NULL;
14367
14368   // ---------- try to replace file extension ----------
14369
14370   filename_prefix = getStringCopy(filename_music);
14371   if (strrchr(filename_prefix, '.') != NULL)
14372     *strrchr(filename_prefix, '.') = '\0';
14373   filename_info = getStringCat2(filename_prefix, ".txt");
14374
14375   if (fileExists(filename_info))
14376     setup_file_hash = loadSetupFileHash(filename_info);
14377
14378   free(filename_prefix);
14379   free(filename_info);
14380
14381   if (setup_file_hash == NULL)
14382   {
14383     // ---------- try to add file extension ----------
14384
14385     filename_prefix = getStringCopy(filename_music);
14386     filename_info = getStringCat2(filename_prefix, ".txt");
14387
14388     if (fileExists(filename_info))
14389       setup_file_hash = loadSetupFileHash(filename_info);
14390
14391     free(filename_prefix);
14392     free(filename_info);
14393   }
14394
14395   if (setup_file_hash == NULL)
14396     return NULL;
14397
14398   // ---------- music file info found ----------
14399
14400   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14401
14402   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14403   {
14404     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14405
14406     *token_to_value_ptr[i].value_ptr =
14407       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14408   }
14409
14410   tmp_music_file_info.basename = getStringCopy(basename);
14411   tmp_music_file_info.music = music;
14412   tmp_music_file_info.is_sound = is_sound;
14413
14414   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14415   *new_music_file_info = tmp_music_file_info;
14416
14417   return new_music_file_info;
14418 }
14419
14420 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14421 {
14422   return get_music_file_info_ext(basename, music, FALSE);
14423 }
14424
14425 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14426 {
14427   return get_music_file_info_ext(basename, sound, TRUE);
14428 }
14429
14430 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14431                                      char *basename, boolean is_sound)
14432 {
14433   for (; list != NULL; list = list->next)
14434     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14435       return TRUE;
14436
14437   return FALSE;
14438 }
14439
14440 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14441 {
14442   return music_info_listed_ext(list, basename, FALSE);
14443 }
14444
14445 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14446 {
14447   return music_info_listed_ext(list, basename, TRUE);
14448 }
14449
14450 void LoadMusicInfo(void)
14451 {
14452   int num_music_noconf = getMusicListSize_NoConf();
14453   int num_music = getMusicListSize();
14454   int num_sounds = getSoundListSize();
14455   struct FileInfo *music, *sound;
14456   struct MusicFileInfo *next, **new;
14457
14458   int i;
14459
14460   while (music_file_info != NULL)
14461   {
14462     next = music_file_info->next;
14463
14464     checked_free(music_file_info->basename);
14465
14466     checked_free(music_file_info->title_header);
14467     checked_free(music_file_info->artist_header);
14468     checked_free(music_file_info->album_header);
14469     checked_free(music_file_info->year_header);
14470     checked_free(music_file_info->played_header);
14471
14472     checked_free(music_file_info->title);
14473     checked_free(music_file_info->artist);
14474     checked_free(music_file_info->album);
14475     checked_free(music_file_info->year);
14476     checked_free(music_file_info->played);
14477
14478     free(music_file_info);
14479
14480     music_file_info = next;
14481   }
14482
14483   new = &music_file_info;
14484
14485   // get (configured or unconfigured) music file info for all levels
14486   for (i = leveldir_current->first_level;
14487        i <= leveldir_current->last_level; i++)
14488   {
14489     int music_nr;
14490
14491     if (levelset.music[i] != MUS_UNDEFINED)
14492     {
14493       // get music file info for configured level music
14494       music_nr = levelset.music[i];
14495     }
14496     else if (num_music_noconf > 0)
14497     {
14498       // get music file info for unconfigured level music
14499       int level_pos = i - leveldir_current->first_level;
14500
14501       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14502     }
14503     else
14504     {
14505       continue;
14506     }
14507
14508     char *basename = getMusicInfoEntryFilename(music_nr);
14509
14510     if (basename == NULL)
14511       continue;
14512
14513     if (!music_info_listed(music_file_info, basename))
14514     {
14515       *new = get_music_file_info(basename, music_nr);
14516
14517       if (*new != NULL)
14518         new = &(*new)->next;
14519     }
14520   }
14521
14522   // get music file info for all remaining configured music files
14523   for (i = 0; i < num_music; i++)
14524   {
14525     music = getMusicListEntry(i);
14526
14527     if (music->filename == NULL)
14528       continue;
14529
14530     if (strEqual(music->filename, UNDEFINED_FILENAME))
14531       continue;
14532
14533     // a configured file may be not recognized as music
14534     if (!FileIsMusic(music->filename))
14535       continue;
14536
14537     if (!music_info_listed(music_file_info, music->filename))
14538     {
14539       *new = get_music_file_info(music->filename, i);
14540
14541       if (*new != NULL)
14542         new = &(*new)->next;
14543     }
14544   }
14545
14546   // get sound file info for all configured sound files
14547   for (i = 0; i < num_sounds; i++)
14548   {
14549     sound = getSoundListEntry(i);
14550
14551     if (sound->filename == NULL)
14552       continue;
14553
14554     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14555       continue;
14556
14557     // a configured file may be not recognized as sound
14558     if (!FileIsSound(sound->filename))
14559       continue;
14560
14561     if (!sound_info_listed(music_file_info, sound->filename))
14562     {
14563       *new = get_sound_file_info(sound->filename, i);
14564       if (*new != NULL)
14565         new = &(*new)->next;
14566     }
14567   }
14568
14569   // add pointers to previous list nodes
14570
14571   struct MusicFileInfo *node = music_file_info;
14572
14573   while (node != NULL)
14574   {
14575     if (node->next)
14576       node->next->prev = node;
14577
14578     node = node->next;
14579   }
14580 }
14581
14582 static void add_helpanim_entry(int element, int action, int direction,
14583                                int delay, int *num_list_entries)
14584 {
14585   struct HelpAnimInfo *new_list_entry;
14586   (*num_list_entries)++;
14587
14588   helpanim_info =
14589     checked_realloc(helpanim_info,
14590                     *num_list_entries * sizeof(struct HelpAnimInfo));
14591   new_list_entry = &helpanim_info[*num_list_entries - 1];
14592
14593   new_list_entry->element = element;
14594   new_list_entry->action = action;
14595   new_list_entry->direction = direction;
14596   new_list_entry->delay = delay;
14597 }
14598
14599 static void print_unknown_token(char *filename, char *token, int token_nr)
14600 {
14601   if (token_nr == 0)
14602   {
14603     Warn("---");
14604     Warn("unknown token(s) found in config file:");
14605     Warn("- config file: '%s'", filename);
14606   }
14607
14608   Warn("- token: '%s'", token);
14609 }
14610
14611 static void print_unknown_token_end(int token_nr)
14612 {
14613   if (token_nr > 0)
14614     Warn("---");
14615 }
14616
14617 void LoadHelpAnimInfo(void)
14618 {
14619   char *filename = getHelpAnimFilename();
14620   SetupFileList *setup_file_list = NULL, *list;
14621   SetupFileHash *element_hash, *action_hash, *direction_hash;
14622   int num_list_entries = 0;
14623   int num_unknown_tokens = 0;
14624   int i;
14625
14626   if (fileExists(filename))
14627     setup_file_list = loadSetupFileList(filename);
14628
14629   if (setup_file_list == NULL)
14630   {
14631     // use reliable default values from static configuration
14632     SetupFileList *insert_ptr;
14633
14634     insert_ptr = setup_file_list =
14635       newSetupFileList(helpanim_config[0].token,
14636                        helpanim_config[0].value);
14637
14638     for (i = 1; helpanim_config[i].token; i++)
14639       insert_ptr = addListEntry(insert_ptr,
14640                                 helpanim_config[i].token,
14641                                 helpanim_config[i].value);
14642   }
14643
14644   element_hash   = newSetupFileHash();
14645   action_hash    = newSetupFileHash();
14646   direction_hash = newSetupFileHash();
14647
14648   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14649     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14650
14651   for (i = 0; i < NUM_ACTIONS; i++)
14652     setHashEntry(action_hash, element_action_info[i].suffix,
14653                  i_to_a(element_action_info[i].value));
14654
14655   // do not store direction index (bit) here, but direction value!
14656   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14657     setHashEntry(direction_hash, element_direction_info[i].suffix,
14658                  i_to_a(1 << element_direction_info[i].value));
14659
14660   for (list = setup_file_list; list != NULL; list = list->next)
14661   {
14662     char *element_token, *action_token, *direction_token;
14663     char *element_value, *action_value, *direction_value;
14664     int delay = atoi(list->value);
14665
14666     if (strEqual(list->token, "end"))
14667     {
14668       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14669
14670       continue;
14671     }
14672
14673     /* first try to break element into element/action/direction parts;
14674        if this does not work, also accept combined "element[.act][.dir]"
14675        elements (like "dynamite.active"), which are unique elements */
14676
14677     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14678     {
14679       element_value = getHashEntry(element_hash, list->token);
14680       if (element_value != NULL)        // element found
14681         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14682                            &num_list_entries);
14683       else
14684       {
14685         // no further suffixes found -- this is not an element
14686         print_unknown_token(filename, list->token, num_unknown_tokens++);
14687       }
14688
14689       continue;
14690     }
14691
14692     // token has format "<prefix>.<something>"
14693
14694     action_token = strchr(list->token, '.');    // suffix may be action ...
14695     direction_token = action_token;             // ... or direction
14696
14697     element_token = getStringCopy(list->token);
14698     *strchr(element_token, '.') = '\0';
14699
14700     element_value = getHashEntry(element_hash, element_token);
14701
14702     if (element_value == NULL)          // this is no element
14703     {
14704       element_value = getHashEntry(element_hash, list->token);
14705       if (element_value != NULL)        // combined element found
14706         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14707                            &num_list_entries);
14708       else
14709         print_unknown_token(filename, list->token, num_unknown_tokens++);
14710
14711       free(element_token);
14712
14713       continue;
14714     }
14715
14716     action_value = getHashEntry(action_hash, action_token);
14717
14718     if (action_value != NULL)           // action found
14719     {
14720       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14721                     &num_list_entries);
14722
14723       free(element_token);
14724
14725       continue;
14726     }
14727
14728     direction_value = getHashEntry(direction_hash, direction_token);
14729
14730     if (direction_value != NULL)        // direction found
14731     {
14732       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14733                          &num_list_entries);
14734
14735       free(element_token);
14736
14737       continue;
14738     }
14739
14740     if (strchr(action_token + 1, '.') == NULL)
14741     {
14742       // no further suffixes found -- this is not an action nor direction
14743
14744       element_value = getHashEntry(element_hash, list->token);
14745       if (element_value != NULL)        // combined element found
14746         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14747                            &num_list_entries);
14748       else
14749         print_unknown_token(filename, list->token, num_unknown_tokens++);
14750
14751       free(element_token);
14752
14753       continue;
14754     }
14755
14756     // token has format "<prefix>.<suffix>.<something>"
14757
14758     direction_token = strchr(action_token + 1, '.');
14759
14760     action_token = getStringCopy(action_token);
14761     *strchr(action_token + 1, '.') = '\0';
14762
14763     action_value = getHashEntry(action_hash, action_token);
14764
14765     if (action_value == NULL)           // this is no action
14766     {
14767       element_value = getHashEntry(element_hash, list->token);
14768       if (element_value != NULL)        // combined element found
14769         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14770                            &num_list_entries);
14771       else
14772         print_unknown_token(filename, list->token, num_unknown_tokens++);
14773
14774       free(element_token);
14775       free(action_token);
14776
14777       continue;
14778     }
14779
14780     direction_value = getHashEntry(direction_hash, direction_token);
14781
14782     if (direction_value != NULL)        // direction found
14783     {
14784       add_helpanim_entry(atoi(element_value), atoi(action_value),
14785                          atoi(direction_value), delay, &num_list_entries);
14786
14787       free(element_token);
14788       free(action_token);
14789
14790       continue;
14791     }
14792
14793     // this is no direction
14794
14795     element_value = getHashEntry(element_hash, list->token);
14796     if (element_value != NULL)          // combined element found
14797       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14798                          &num_list_entries);
14799     else
14800       print_unknown_token(filename, list->token, num_unknown_tokens++);
14801
14802     free(element_token);
14803     free(action_token);
14804   }
14805
14806   print_unknown_token_end(num_unknown_tokens);
14807
14808   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14809   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14810
14811   freeSetupFileList(setup_file_list);
14812   freeSetupFileHash(element_hash);
14813   freeSetupFileHash(action_hash);
14814   freeSetupFileHash(direction_hash);
14815
14816 #if 0
14817   for (i = 0; i < num_list_entries; i++)
14818     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14819           EL_NAME(helpanim_info[i].element),
14820           helpanim_info[i].element,
14821           helpanim_info[i].action,
14822           helpanim_info[i].direction,
14823           helpanim_info[i].delay);
14824 #endif
14825 }
14826
14827 void LoadHelpTextInfo(void)
14828 {
14829   char *filename = getHelpTextFilename();
14830   int i;
14831
14832   if (helptext_info != NULL)
14833   {
14834     freeSetupFileHash(helptext_info);
14835     helptext_info = NULL;
14836   }
14837
14838   if (fileExists(filename))
14839     helptext_info = loadSetupFileHash(filename);
14840
14841   if (helptext_info == NULL)
14842   {
14843     // use reliable default values from static configuration
14844     helptext_info = newSetupFileHash();
14845
14846     for (i = 0; helptext_config[i].token; i++)
14847       setHashEntry(helptext_info,
14848                    helptext_config[i].token,
14849                    helptext_config[i].value);
14850   }
14851
14852 #if 0
14853   BEGIN_HASH_ITERATION(helptext_info, itr)
14854   {
14855     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14856           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14857   }
14858   END_HASH_ITERATION(hash, itr)
14859 #endif
14860 }
14861
14862
14863 // ----------------------------------------------------------------------------
14864 // convert levels
14865 // ----------------------------------------------------------------------------
14866
14867 #define MAX_NUM_CONVERT_LEVELS          1000
14868
14869 void ConvertLevels(void)
14870 {
14871   static LevelDirTree *convert_leveldir = NULL;
14872   static int convert_level_nr = -1;
14873   static int num_levels_handled = 0;
14874   static int num_levels_converted = 0;
14875   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14876   int i;
14877
14878   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14879                                                global.convert_leveldir);
14880
14881   if (convert_leveldir == NULL)
14882     Fail("no such level identifier: '%s'", global.convert_leveldir);
14883
14884   leveldir_current = convert_leveldir;
14885
14886   if (global.convert_level_nr != -1)
14887   {
14888     convert_leveldir->first_level = global.convert_level_nr;
14889     convert_leveldir->last_level  = global.convert_level_nr;
14890   }
14891
14892   convert_level_nr = convert_leveldir->first_level;
14893
14894   PrintLine("=", 79);
14895   Print("Converting levels\n");
14896   PrintLine("-", 79);
14897   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14898   Print("Level series name:       '%s'\n", convert_leveldir->name);
14899   Print("Level series author:     '%s'\n", convert_leveldir->author);
14900   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14901   PrintLine("=", 79);
14902   Print("\n");
14903
14904   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14905     levels_failed[i] = FALSE;
14906
14907   while (convert_level_nr <= convert_leveldir->last_level)
14908   {
14909     char *level_filename;
14910     boolean new_level;
14911
14912     level_nr = convert_level_nr++;
14913
14914     Print("Level %03d: ", level_nr);
14915
14916     LoadLevel(level_nr);
14917     if (level.no_level_file || level.no_valid_file)
14918     {
14919       Print("(no level)\n");
14920       continue;
14921     }
14922
14923     Print("converting level ... ");
14924
14925 #if 0
14926     // special case: conversion of some EMC levels as requested by ACME
14927     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14928 #endif
14929
14930     level_filename = getDefaultLevelFilename(level_nr);
14931     new_level = !fileExists(level_filename);
14932
14933     if (new_level)
14934     {
14935       SaveLevel(level_nr);
14936
14937       num_levels_converted++;
14938
14939       Print("converted.\n");
14940     }
14941     else
14942     {
14943       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14944         levels_failed[level_nr] = TRUE;
14945
14946       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14947     }
14948
14949     num_levels_handled++;
14950   }
14951
14952   Print("\n");
14953   PrintLine("=", 79);
14954   Print("Number of levels handled: %d\n", num_levels_handled);
14955   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14956          (num_levels_handled ?
14957           num_levels_converted * 100 / num_levels_handled : 0));
14958   PrintLine("-", 79);
14959   Print("Summary (for automatic parsing by scripts):\n");
14960   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14961          convert_leveldir->identifier, num_levels_converted,
14962          num_levels_handled,
14963          (num_levels_handled ?
14964           num_levels_converted * 100 / num_levels_handled : 0));
14965
14966   if (num_levels_handled != num_levels_converted)
14967   {
14968     Print(", FAILED:");
14969     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14970       if (levels_failed[i])
14971         Print(" %03d", i);
14972   }
14973
14974   Print("\n");
14975   PrintLine("=", 79);
14976
14977   CloseAllAndExit(0);
14978 }
14979
14980
14981 // ----------------------------------------------------------------------------
14982 // create and save images for use in level sketches (raw BMP format)
14983 // ----------------------------------------------------------------------------
14984
14985 void CreateLevelSketchImages(void)
14986 {
14987   Bitmap *bitmap1;
14988   Bitmap *bitmap2;
14989   int i;
14990
14991   InitElementPropertiesGfxElement();
14992
14993   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14994   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14995
14996   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14997   {
14998     int element = getMappedElement(i);
14999     char basename1[16];
15000     char basename2[16];
15001     char *filename1;
15002     char *filename2;
15003
15004     sprintf(basename1, "%04d.bmp", i);
15005     sprintf(basename2, "%04ds.bmp", i);
15006
15007     filename1 = getPath2(global.create_sketch_images_dir, basename1);
15008     filename2 = getPath2(global.create_sketch_images_dir, basename2);
15009
15010     DrawSizedElement(0, 0, element, TILESIZE);
15011     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
15012
15013     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
15014       Fail("cannot save level sketch image file '%s'", filename1);
15015
15016     DrawSizedElement(0, 0, element, MINI_TILESIZE);
15017     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
15018
15019     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
15020       Fail("cannot save level sketch image file '%s'", filename2);
15021
15022     free(filename1);
15023     free(filename2);
15024
15025     // create corresponding SQL statements (for normal and small images)
15026     if (i < 1000)
15027     {
15028       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15029       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15030     }
15031
15032     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
15033     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
15034
15035     // optional: create content for forum level sketch demonstration post
15036     if (options.debug)
15037       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
15038   }
15039
15040   FreeBitmap(bitmap1);
15041   FreeBitmap(bitmap2);
15042
15043   if (options.debug)
15044     fprintf(stderr, "\n");
15045
15046   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
15047
15048   CloseAllAndExit(0);
15049 }
15050
15051
15052 // ----------------------------------------------------------------------------
15053 // create and save images for element collecting animations (raw BMP format)
15054 // ----------------------------------------------------------------------------
15055
15056 static boolean createCollectImage(int element)
15057 {
15058   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
15059 }
15060
15061 void CreateCollectElementImages(void)
15062 {
15063   int i, j;
15064   int num_steps = 8;
15065   int anim_frames = num_steps - 1;
15066   int tile_size = TILESIZE;
15067   int anim_width  = tile_size * anim_frames;
15068   int anim_height = tile_size;
15069   int num_collect_images = 0;
15070   int pos_collect_images = 0;
15071
15072   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15073     if (createCollectImage(i))
15074       num_collect_images++;
15075
15076   Info("Creating %d element collecting animation images ...",
15077        num_collect_images);
15078
15079   int dst_width  = anim_width * 2;
15080   int dst_height = anim_height * num_collect_images / 2;
15081   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
15082   char *basename_bmp = "RocksCollect.bmp";
15083   char *basename_png = "RocksCollect.png";
15084   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
15085   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
15086   int len_filename_bmp = strlen(filename_bmp);
15087   int len_filename_png = strlen(filename_png);
15088   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
15089   char cmd_convert[max_command_len];
15090
15091   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
15092            filename_bmp,
15093            filename_png);
15094
15095   // force using RGBA surface for destination bitmap
15096   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
15097                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
15098
15099   dst_bitmap->surface =
15100     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15101
15102   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
15103   {
15104     if (!createCollectImage(i))
15105       continue;
15106
15107     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
15108     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
15109     int graphic = el2img(i);
15110     char *token_name = element_info[i].token_name;
15111     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
15112     Bitmap *src_bitmap;
15113     int src_x, src_y;
15114
15115     Info("- creating collecting image for '%s' ...", token_name);
15116
15117     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
15118
15119     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
15120                tile_size, tile_size, 0, 0);
15121
15122     // force using RGBA surface for temporary bitmap (using transparent black)
15123     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
15124                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
15125
15126     tmp_bitmap->surface =
15127       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
15128
15129     tmp_bitmap->surface_masked = tmp_bitmap->surface;
15130
15131     for (j = 0; j < anim_frames; j++)
15132     {
15133       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
15134       int frame_size = frame_size_final * num_steps;
15135       int offset = (tile_size - frame_size_final) / 2;
15136       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
15137
15138       while (frame_size > frame_size_final)
15139       {
15140         frame_size /= 2;
15141
15142         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
15143
15144         FreeBitmap(frame_bitmap);
15145
15146         frame_bitmap = half_bitmap;
15147       }
15148
15149       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
15150                        frame_size_final, frame_size_final,
15151                        dst_x + j * tile_size + offset, dst_y + offset);
15152
15153       FreeBitmap(frame_bitmap);
15154     }
15155
15156     tmp_bitmap->surface_masked = NULL;
15157
15158     FreeBitmap(tmp_bitmap);
15159
15160     pos_collect_images++;
15161   }
15162
15163   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
15164     Fail("cannot save element collecting image file '%s'", filename_bmp);
15165
15166   FreeBitmap(dst_bitmap);
15167
15168   Info("Converting image file from BMP to PNG ...");
15169
15170   if (system(cmd_convert) != 0)
15171     Fail("converting image file failed");
15172
15173   unlink(filename_bmp);
15174
15175   Info("Done.");
15176
15177   CloseAllAndExit(0);
15178 }
15179
15180
15181 // ----------------------------------------------------------------------------
15182 // create and save images for custom and group elements (raw BMP format)
15183 // ----------------------------------------------------------------------------
15184
15185 void CreateCustomElementImages(char *directory)
15186 {
15187   char *src_basename = "RocksCE-template.ilbm";
15188   char *dst_basename = "RocksCE.bmp";
15189   char *src_filename = getPath2(directory, src_basename);
15190   char *dst_filename = getPath2(directory, dst_basename);
15191   Bitmap *src_bitmap;
15192   Bitmap *bitmap;
15193   int yoffset_ce = 0;
15194   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15195   int i;
15196
15197   InitVideoDefaults();
15198
15199   ReCreateBitmap(&backbuffer, video.width, video.height);
15200
15201   src_bitmap = LoadImage(src_filename);
15202
15203   bitmap = CreateBitmap(TILEX * 16 * 2,
15204                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15205                         DEFAULT_DEPTH);
15206
15207   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15208   {
15209     int x = i % 16;
15210     int y = i / 16;
15211     int ii = i + 1;
15212     int j;
15213
15214     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15215                TILEX * x, TILEY * y + yoffset_ce);
15216
15217     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15218                TILEX, TILEY,
15219                TILEX * x + TILEX * 16,
15220                TILEY * y + yoffset_ce);
15221
15222     for (j = 2; j >= 0; j--)
15223     {
15224       int c = ii % 10;
15225
15226       BlitBitmap(src_bitmap, bitmap,
15227                  TILEX + c * 7, 0, 6, 10,
15228                  TILEX * x + 6 + j * 7,
15229                  TILEY * y + 11 + yoffset_ce);
15230
15231       BlitBitmap(src_bitmap, bitmap,
15232                  TILEX + c * 8, TILEY, 6, 10,
15233                  TILEX * 16 + TILEX * x + 6 + j * 8,
15234                  TILEY * y + 10 + yoffset_ce);
15235
15236       ii /= 10;
15237     }
15238   }
15239
15240   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15241   {
15242     int x = i % 16;
15243     int y = i / 16;
15244     int ii = i + 1;
15245     int j;
15246
15247     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15248                TILEX * x, TILEY * y + yoffset_ge);
15249
15250     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15251                TILEX, TILEY,
15252                TILEX * x + TILEX * 16,
15253                TILEY * y + yoffset_ge);
15254
15255     for (j = 1; j >= 0; j--)
15256     {
15257       int c = ii % 10;
15258
15259       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15260                  TILEX * x + 6 + j * 10,
15261                  TILEY * y + 11 + yoffset_ge);
15262
15263       BlitBitmap(src_bitmap, bitmap,
15264                  TILEX + c * 8, TILEY + 12, 6, 10,
15265                  TILEX * 16 + TILEX * x + 10 + j * 8,
15266                  TILEY * y + 10 + yoffset_ge);
15267
15268       ii /= 10;
15269     }
15270   }
15271
15272   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15273     Fail("cannot save CE graphics file '%s'", dst_filename);
15274
15275   FreeBitmap(bitmap);
15276
15277   CloseAllAndExit(0);
15278 }