added setup option to select default game engine type
[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,              200
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_BD_PLAYER,                       -1,
644     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
645     &li.bd_diagonal_movements,          FALSE
646   },
647   {
648     EL_BD_PLAYER,                       -1,
649     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
650     &li.bd_topmost_player_active,       TRUE
651   },
652   {
653     EL_BD_PLAYER,                       -1,
654     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
655     &li.bd_pushing_prob,                25
656   },
657   {
658     EL_BD_PLAYER,                       -1,
659     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
660     &li.bd_pushing_prob_with_sweet,     100
661   },
662   {
663     EL_BD_PLAYER,                       -1,
664     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
665     &li.bd_push_mega_rock_with_sweet,   FALSE
666   },
667   {
668     EL_BD_PLAYER,                       -1,
669     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
670     &li.bd_snap_element,                EL_EMPTY
671   },
672
673   {
674     EL_BD_SAND,                         -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_sand_looks_like,             EL_BD_SAND
677   },
678
679   {
680     EL_BD_ROCK,                         -1,
681     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
682     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
683   },
684   {
685     EL_BD_ROCK,                         -1,
686     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
687     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
688   },
689
690   {
691     EL_BD_DIAMOND,                      -1,
692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
693     &li.score[SC_DIAMOND_EXTRA],        20
694   },
695   {
696     EL_BD_DIAMOND,                      -1,
697     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
698     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
699   },
700   {
701     EL_BD_DIAMOND,                      -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
703     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
704   },
705
706   {
707     EL_BD_FIREFLY,                      -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
710   },
711
712   {
713     EL_BD_FIREFLY_2,                    -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
716   },
717
718   {
719     EL_BD_BUTTERFLY,                    -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
722   },
723
724   {
725     EL_BD_BUTTERFLY_2,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
728   },
729
730   {
731     EL_BD_STONEFLY,                     -1,
732     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
733     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
734   },
735
736   {
737     EL_BD_DRAGONFLY,                    -1,
738     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
739     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
740   },
741
742   {
743     EL_BD_DIAMOND_GROWING_5,            -1,
744     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
745     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
746   },
747
748   {
749     EL_BD_BOMB_EXPLODING_4,             -1,
750     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
751     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
752   },
753
754   {
755     EL_BD_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_BD_EXPLODING_5,                  -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
763     &li.bd_explosion_turns_to,          EL_EMPTY
764   },
765
766   {
767     EL_BD_MAGIC_WALL,                   -1,
768     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
769     &li.bd_magic_wall_wait_hatching,    FALSE
770   },
771   {
772     EL_BD_MAGIC_WALL,                   -1,
773     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
774     &li.bd_magic_wall_stops_amoeba,     TRUE
775   },
776   {
777     EL_BD_MAGIC_WALL,                   -1,
778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
779     &li.bd_magic_wall_zero_infinite,    TRUE
780   },
781   {
782     EL_BD_MAGIC_WALL,                   -1,
783     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
784     &li.bd_magic_wall_break_scan,       FALSE
785   },
786   {
787     EL_BD_MAGIC_WALL,                   -1,
788     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
789     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
790   },
791   {
792     EL_BD_MAGIC_WALL,                   -1,
793     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
794     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
795   },
796   {
797     EL_BD_MAGIC_WALL,                   -1,
798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
799     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
800   },
801   {
802     EL_BD_MAGIC_WALL,                   -1,
803     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
804     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
805   },
806   {
807     EL_BD_MAGIC_WALL,                   -1,
808     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
809     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
810   },
811   {
812     EL_BD_MAGIC_WALL,                   -1,
813     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
814     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
815   },
816   {
817     EL_BD_MAGIC_WALL,                   -1,
818     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
819     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
820   },
821
822   {
823     EL_BD_CLOCK,                        -1,
824     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
825     &li.bd_clock_extra_time,            30
826   },
827
828   {
829     EL_BD_VOODOO_DOLL,                  -1,
830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
831     &li.bd_voodoo_collects_diamonds,    FALSE
832   },
833   {
834     EL_BD_VOODOO_DOLL,                  -1,
835     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
836     &li.bd_voodoo_hurt_kills_player,    FALSE
837   },
838   {
839     EL_BD_VOODOO_DOLL,                  -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
841     &li.bd_voodoo_dies_by_rock,         FALSE
842   },
843   {
844     EL_BD_VOODOO_DOLL,                  -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
846     &li.bd_voodoo_vanish_by_explosion,  TRUE
847   },
848   {
849     EL_BD_VOODOO_DOLL,                  -1,
850     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
851     &li.bd_voodoo_penalty_time,         30
852   },
853
854   {
855     EL_BD_SLIME,                        -1,
856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
857     &li.bd_slime_is_predictable,        TRUE
858   },
859   {
860     EL_BD_SLIME,                        -1,
861     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
862     &li.bd_slime_permeability_rate,     100
863   },
864   {
865     EL_BD_SLIME,                        -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
867     &li.bd_slime_permeability_bits_c64, 0
868   },
869   {
870     EL_BD_SLIME,                        -1,
871     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
872     &li.bd_slime_random_seed_c64,       -1
873   },
874   {
875     EL_BD_SLIME,                        -1,
876     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
877     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
878   },
879   {
880     EL_BD_SLIME,                        -1,
881     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
882     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
883   },
884   {
885     EL_BD_SLIME,                        -1,
886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
887     &li.bd_slime_eats_element_2,        EL_BD_ROCK
888   },
889   {
890     EL_BD_SLIME,                        -1,
891     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
892     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
893   },
894   {
895     EL_BD_SLIME,                        -1,
896     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
897     &li.bd_slime_eats_element_3,        EL_BD_NUT
898   },
899   {
900     EL_BD_SLIME,                        -1,
901     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
902     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
903   },
904
905   {
906     EL_BD_ACID,                         -1,
907     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
908     &li.bd_acid_eats_element,           EL_BD_SAND
909   },
910   {
911     EL_BD_ACID,                         -1,
912     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
913     &li.bd_acid_spread_rate,            3
914   },
915   {
916     EL_BD_ACID,                         -1,
917     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
918     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
919   },
920
921   {
922     EL_BD_BITER,                        -1,
923     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
924     &li.bd_biter_move_delay,            0
925   },
926   {
927     EL_BD_BITER,                        -1,
928     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
929     &li.bd_biter_eats_element,          EL_BD_DIAMOND
930   },
931
932   {
933     EL_BD_BLADDER,                      -1,
934     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
935     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
936   },
937
938   {
939     EL_BD_EXPANDABLE_WALL_ANY,          -1,
940     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
941     &li.bd_change_expanding_wall,       FALSE
942   },
943   {
944     EL_BD_EXPANDABLE_WALL_ANY,          -1,
945     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
946     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
947   },
948
949   {
950     EL_BD_REPLICATOR,                   -1,
951     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
952     &li.bd_replicators_active,          TRUE
953   },
954   {
955     EL_BD_REPLICATOR,                   -1,
956     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
957     &li.bd_replicator_create_delay,     4
958   },
959
960   {
961     EL_BD_CONVEYOR_LEFT,                -1,
962     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
963     &li.bd_conveyor_belts_active,       TRUE
964   },
965   {
966     EL_BD_CONVEYOR_LEFT,                -1,
967     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
968     &li.bd_conveyor_belts_changed,      FALSE
969   },
970
971   {
972     EL_BD_WATER,                        -1,
973     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
974     &li.bd_water_cannot_flow_down,      FALSE
975   },
976
977   {
978     EL_BD_NUT,                          -1,
979     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
980     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
981   },
982
983   {
984     EL_BD_PNEUMATIC_HAMMER,             -1,
985     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
986     &li.bd_hammer_walls_break_delay,    5
987   },
988   {
989     EL_BD_PNEUMATIC_HAMMER,             -1,
990     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
991     &li.bd_hammer_walls_reappear,       FALSE
992   },
993   {
994     EL_BD_PNEUMATIC_HAMMER,             -1,
995     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
996     &li.bd_hammer_walls_reappear_delay, 100
997   },
998
999   {
1000     EL_BD_ROCKET_LAUNCHER,              -1,
1001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1002     &li.bd_infinite_rockets,            FALSE
1003   },
1004
1005   {
1006     EL_BD_SKELETON,                     -1,
1007     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1008     &li.bd_num_skeletons_needed_for_pot, 5
1009   },
1010   {
1011     EL_BD_SKELETON,                     -1,
1012     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1013     &li.bd_skeleton_worth_num_diamonds, 0
1014   },
1015
1016   {
1017     EL_BD_CREATURE_SWITCH,              -1,
1018     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1019     &li.bd_creatures_start_backwards,   FALSE
1020   },
1021   {
1022     EL_BD_CREATURE_SWITCH,              -1,
1023     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1024     &li.bd_creatures_turn_on_hatching,  FALSE
1025   },
1026   {
1027     EL_BD_CREATURE_SWITCH,              -1,
1028     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1029     &li.bd_creatures_auto_turn_delay,   0
1030   },
1031
1032   {
1033     EL_BD_GRAVITY_SWITCH,               -1,
1034     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1035     &li.bd_gravity_direction,           GD_MV_DOWN
1036   },
1037   {
1038     EL_BD_GRAVITY_SWITCH,               -1,
1039     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1040     &li.bd_gravity_switch_active,       FALSE
1041   },
1042   {
1043     EL_BD_GRAVITY_SWITCH,               -1,
1044     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1045     &li.bd_gravity_switch_delay,        10
1046   },
1047   {
1048     EL_BD_GRAVITY_SWITCH,               -1,
1049     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1050     &li.bd_gravity_affects_all,         TRUE
1051   },
1052
1053   // (the following values are related to various game elements)
1054
1055   {
1056     EL_EMERALD,                         -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.score[SC_EMERALD],              10
1059   },
1060
1061   {
1062     EL_DIAMOND,                         -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.score[SC_DIAMOND],              10
1065   },
1066
1067   {
1068     EL_BUG,                             -1,
1069     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1070     &li.score[SC_BUG],                  10
1071   },
1072
1073   {
1074     EL_SPACESHIP,                       -1,
1075     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1076     &li.score[SC_SPACESHIP],            10
1077   },
1078
1079   {
1080     EL_PACMAN,                          -1,
1081     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1082     &li.score[SC_PACMAN],               10
1083   },
1084
1085   {
1086     EL_NUT,                             -1,
1087     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1088     &li.score[SC_NUT],                  10
1089   },
1090
1091   {
1092     EL_DYNAMITE,                        -1,
1093     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1094     &li.score[SC_DYNAMITE],             10
1095   },
1096
1097   {
1098     EL_KEY_1,                           -1,
1099     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1100     &li.score[SC_KEY],                  10
1101   },
1102
1103   {
1104     EL_PEARL,                           -1,
1105     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1106     &li.score[SC_PEARL],                10
1107   },
1108
1109   {
1110     EL_CRYSTAL,                         -1,
1111     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1112     &li.score[SC_CRYSTAL],              10
1113   },
1114
1115   // (amoeba values used by R'n'D game engine only)
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1119     &li.amoeba_content,                 EL_DIAMOND
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1124     &li.amoeba_speed,                   10
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1129     &li.grow_into_diggable,             TRUE
1130   },
1131   // (amoeba values used by BD game engine only)
1132   {
1133     EL_BD_AMOEBA,                       -1,
1134     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1135     &li.bd_amoeba_wait_for_hatching,    FALSE
1136   },
1137   {
1138     EL_BD_AMOEBA,                       -1,
1139     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1140     &li.bd_amoeba_start_immediately,    TRUE
1141   },
1142   {
1143     EL_BD_AMOEBA,                       -1,
1144     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1145     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1146   },
1147   {
1148     EL_BD_AMOEBA,                       -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1150     &li.bd_amoeba_threshold_too_big,    200
1151   },
1152   {
1153     EL_BD_AMOEBA,                       -1,
1154     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1155     &li.bd_amoeba_slow_growth_time,     200
1156   },
1157   {
1158     EL_BD_AMOEBA,                       -1,
1159     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1160     &li.bd_amoeba_slow_growth_rate,     3
1161   },
1162   {
1163     EL_BD_AMOEBA,                       -1,
1164     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1165     &li.bd_amoeba_fast_growth_rate,     25
1166   },
1167   {
1168     EL_BD_AMOEBA,                       -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1170     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1171   },
1172   {
1173     EL_BD_AMOEBA,                       -1,
1174     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1175     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1176   },
1177
1178   {
1179     EL_BD_AMOEBA_2,                     -1,
1180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1181     &li.bd_amoeba_2_threshold_too_big,  200
1182   },
1183   {
1184     EL_BD_AMOEBA_2,                     -1,
1185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1186     &li.bd_amoeba_2_slow_growth_time,   200
1187   },
1188   {
1189     EL_BD_AMOEBA_2,                     -1,
1190     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1191     &li.bd_amoeba_2_slow_growth_rate,   3
1192   },
1193   {
1194     EL_BD_AMOEBA_2,                     -1,
1195     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1196     &li.bd_amoeba_2_fast_growth_rate,   25
1197   },
1198   {
1199     EL_BD_AMOEBA_2,                     -1,
1200     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1201     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1202   },
1203   {
1204     EL_BD_AMOEBA_2,                     -1,
1205     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1206     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1207   },
1208   {
1209     EL_BD_AMOEBA_2,                     -1,
1210     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1211     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1212   },
1213   {
1214     EL_BD_AMOEBA_2,                     -1,
1215     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1216     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1217   },
1218
1219   {
1220     EL_YAMYAM,                          -1,
1221     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1222     &li.yamyam_content,                 EL_ROCK, NULL,
1223     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1224   },
1225   {
1226     EL_YAMYAM,                          -1,
1227     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1228     &li.score[SC_YAMYAM],               10
1229   },
1230
1231   {
1232     EL_ROBOT,                           -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1234     &li.score[SC_ROBOT],                10
1235   },
1236   {
1237     EL_ROBOT,                           -1,
1238     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1239     &li.slurp_score,                    10
1240   },
1241
1242   {
1243     EL_ROBOT_WHEEL,                     -1,
1244     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1245     &li.time_wheel,                     10
1246   },
1247
1248   {
1249     EL_MAGIC_WALL,                      -1,
1250     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1251     &li.time_magic_wall,                10
1252   },
1253
1254   {
1255     EL_GAME_OF_LIFE,                    -1,
1256     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1257     &li.game_of_life[0],                2
1258   },
1259   {
1260     EL_GAME_OF_LIFE,                    -1,
1261     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1262     &li.game_of_life[1],                3
1263   },
1264   {
1265     EL_GAME_OF_LIFE,                    -1,
1266     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1267     &li.game_of_life[2],                3
1268   },
1269   {
1270     EL_GAME_OF_LIFE,                    -1,
1271     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1272     &li.game_of_life[3],                3
1273   },
1274   {
1275     EL_GAME_OF_LIFE,                    -1,
1276     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1277     &li.use_life_bugs,                  FALSE
1278   },
1279
1280   {
1281     EL_BIOMAZE,                         -1,
1282     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1283     &li.biomaze[0],                     2
1284   },
1285   {
1286     EL_BIOMAZE,                         -1,
1287     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1288     &li.biomaze[1],                     3
1289   },
1290   {
1291     EL_BIOMAZE,                         -1,
1292     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1293     &li.biomaze[2],                     3
1294   },
1295   {
1296     EL_BIOMAZE,                         -1,
1297     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1298     &li.biomaze[3],                     3
1299   },
1300
1301   {
1302     EL_TIMEGATE_SWITCH,                 -1,
1303     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1304     &li.time_timegate,                  10
1305   },
1306
1307   {
1308     EL_LIGHT_SWITCH_ACTIVE,             -1,
1309     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1310     &li.time_light,                     10
1311   },
1312
1313   {
1314     EL_SHIELD_NORMAL,                   -1,
1315     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1316     &li.shield_normal_time,             10
1317   },
1318   {
1319     EL_SHIELD_NORMAL,                   -1,
1320     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1321     &li.score[SC_SHIELD],               10
1322   },
1323
1324   {
1325     EL_SHIELD_DEADLY,                   -1,
1326     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1327     &li.shield_deadly_time,             10
1328   },
1329   {
1330     EL_SHIELD_DEADLY,                   -1,
1331     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1332     &li.score[SC_SHIELD],               10
1333   },
1334
1335   {
1336     EL_EXTRA_TIME,                      -1,
1337     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1338     &li.extra_time,                     10
1339   },
1340   {
1341     EL_EXTRA_TIME,                      -1,
1342     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1343     &li.extra_time_score,               10
1344   },
1345
1346   {
1347     EL_TIME_ORB_FULL,                   -1,
1348     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1349     &li.time_orb_time,                  10
1350   },
1351   {
1352     EL_TIME_ORB_FULL,                   -1,
1353     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1354     &li.use_time_orb_bug,               FALSE
1355   },
1356
1357   {
1358     EL_SPRING,                          -1,
1359     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1360     &li.use_spring_bug,                 FALSE
1361   },
1362
1363   {
1364     EL_EMC_ANDROID,                     -1,
1365     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1366     &li.android_move_time,              10
1367   },
1368   {
1369     EL_EMC_ANDROID,                     -1,
1370     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1371     &li.android_clone_time,             10
1372   },
1373   {
1374     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1375     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1376     &li.android_clone_element[0],       EL_EMPTY, NULL,
1377     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1378   },
1379   {
1380     EL_EMC_ANDROID,                     -1,
1381     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1382     &li.android_clone_element[0],       EL_EMPTY, NULL,
1383     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1384   },
1385
1386   {
1387     EL_EMC_LENSES,                      -1,
1388     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1389     &li.lenses_score,                   10
1390   },
1391   {
1392     EL_EMC_LENSES,                      -1,
1393     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1394     &li.lenses_time,                    10
1395   },
1396
1397   {
1398     EL_EMC_MAGNIFIER,                   -1,
1399     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1400     &li.magnify_score,                  10
1401   },
1402   {
1403     EL_EMC_MAGNIFIER,                   -1,
1404     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1405     &li.magnify_time,                   10
1406   },
1407
1408   {
1409     EL_EMC_MAGIC_BALL,                  -1,
1410     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1411     &li.ball_time,                      10
1412   },
1413   {
1414     EL_EMC_MAGIC_BALL,                  -1,
1415     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1416     &li.ball_random,                    FALSE
1417   },
1418   {
1419     EL_EMC_MAGIC_BALL,                  -1,
1420     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1421     &li.ball_active_initial,            FALSE
1422   },
1423   {
1424     EL_EMC_MAGIC_BALL,                  -1,
1425     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1426     &li.ball_content,                   EL_EMPTY, NULL,
1427     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1428   },
1429
1430   {
1431     EL_SOKOBAN_FIELD_EMPTY,             -1,
1432     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1433     &li.sb_fields_needed,               TRUE
1434   },
1435
1436   {
1437     EL_SOKOBAN_OBJECT,                  -1,
1438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1439     &li.sb_objects_needed,              TRUE
1440   },
1441
1442   {
1443     EL_MM_MCDUFFIN,                     -1,
1444     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1445     &li.mm_laser_red,                   FALSE
1446   },
1447   {
1448     EL_MM_MCDUFFIN,                     -1,
1449     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1450     &li.mm_laser_green,                 FALSE
1451   },
1452   {
1453     EL_MM_MCDUFFIN,                     -1,
1454     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1455     &li.mm_laser_blue,                  TRUE
1456   },
1457
1458   {
1459     EL_DF_LASER,                        -1,
1460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1461     &li.df_laser_red,                   TRUE
1462   },
1463   {
1464     EL_DF_LASER,                        -1,
1465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1466     &li.df_laser_green,                 TRUE
1467   },
1468   {
1469     EL_DF_LASER,                        -1,
1470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1471     &li.df_laser_blue,                  FALSE
1472   },
1473
1474   {
1475     EL_MM_FUSE_ACTIVE,                  -1,
1476     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1477     &li.mm_time_fuse,                   25
1478   },
1479   {
1480     EL_MM_BOMB,                         -1,
1481     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1482     &li.mm_time_bomb,                   75
1483   },
1484
1485   {
1486     EL_MM_GRAY_BALL,                    -1,
1487     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1488     &li.mm_time_ball,                   75
1489   },
1490   {
1491     EL_MM_GRAY_BALL,                    -1,
1492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1493     &li.mm_ball_choice_mode,            ANIM_RANDOM
1494   },
1495   {
1496     EL_MM_GRAY_BALL,                    -1,
1497     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1498     &li.mm_ball_content,                EL_EMPTY, NULL,
1499     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1500   },
1501   {
1502     EL_MM_GRAY_BALL,                    -1,
1503     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1504     &li.rotate_mm_ball_content,         TRUE
1505   },
1506   {
1507     EL_MM_GRAY_BALL,                    -1,
1508     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1509     &li.explode_mm_ball,                FALSE
1510   },
1511
1512   {
1513     EL_MM_STEEL_BLOCK,                  -1,
1514     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1515     &li.mm_time_block,                  75
1516   },
1517   {
1518     EL_MM_LIGHTBALL,                    -1,
1519     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1520     &li.score[SC_ELEM_BONUS],           10
1521   },
1522
1523   {
1524     -1,                                 -1,
1525     -1,                                 -1,
1526     NULL,                               -1
1527   }
1528 };
1529
1530 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1531 {
1532   {
1533     -1,                                 -1,
1534     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1535     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1536   },
1537   {
1538     -1,                                 -1,
1539     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1540     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1541   },
1542
1543   {
1544     -1,                                 -1,
1545     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1546     &xx_envelope.autowrap,              FALSE
1547   },
1548   {
1549     -1,                                 -1,
1550     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1551     &xx_envelope.centered,              FALSE
1552   },
1553
1554   {
1555     -1,                                 -1,
1556     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1557     &xx_envelope.text,                  -1, NULL,
1558     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1559     &xx_default_string_empty[0]
1560   },
1561
1562   {
1563     -1,                                 -1,
1564     -1,                                 -1,
1565     NULL,                               -1
1566   }
1567 };
1568
1569 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1570 {
1571   {
1572     -1,                                 -1,
1573     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1574     &xx_ei.description[0],              -1,
1575     &yy_ei.description[0],
1576     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1577     &xx_default_description[0]
1578   },
1579
1580   {
1581     -1,                                 -1,
1582     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1583     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1584     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1585   },
1586 #if ENABLE_RESERVED_CODE
1587   // (reserved for later use)
1588   {
1589     -1,                                 -1,
1590     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1591     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1592     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1593   },
1594 #endif
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1599     &xx_ei.use_gfx_element,             FALSE,
1600     &yy_ei.use_gfx_element
1601   },
1602   {
1603     -1,                                 -1,
1604     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1605     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1606     &yy_ei.gfx_element_initial
1607   },
1608
1609   {
1610     -1,                                 -1,
1611     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1612     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1613     &yy_ei.access_direction
1614   },
1615
1616   {
1617     -1,                                 -1,
1618     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1619     &xx_ei.collect_score_initial,       10,
1620     &yy_ei.collect_score_initial
1621   },
1622   {
1623     -1,                                 -1,
1624     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1625     &xx_ei.collect_count_initial,       1,
1626     &yy_ei.collect_count_initial
1627   },
1628
1629   {
1630     -1,                                 -1,
1631     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1632     &xx_ei.ce_value_fixed_initial,      0,
1633     &yy_ei.ce_value_fixed_initial
1634   },
1635   {
1636     -1,                                 -1,
1637     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1638     &xx_ei.ce_value_random_initial,     0,
1639     &yy_ei.ce_value_random_initial
1640   },
1641   {
1642     -1,                                 -1,
1643     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1644     &xx_ei.use_last_ce_value,           FALSE,
1645     &yy_ei.use_last_ce_value
1646   },
1647
1648   {
1649     -1,                                 -1,
1650     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1651     &xx_ei.push_delay_fixed,            8,
1652     &yy_ei.push_delay_fixed
1653   },
1654   {
1655     -1,                                 -1,
1656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1657     &xx_ei.push_delay_random,           8,
1658     &yy_ei.push_delay_random
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1663     &xx_ei.drop_delay_fixed,            0,
1664     &yy_ei.drop_delay_fixed
1665   },
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1669     &xx_ei.drop_delay_random,           0,
1670     &yy_ei.drop_delay_random
1671   },
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1675     &xx_ei.move_delay_fixed,            0,
1676     &yy_ei.move_delay_fixed
1677   },
1678   {
1679     -1,                                 -1,
1680     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1681     &xx_ei.move_delay_random,           0,
1682     &yy_ei.move_delay_random
1683   },
1684   {
1685     -1,                                 -1,
1686     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1687     &xx_ei.step_delay_fixed,            0,
1688     &yy_ei.step_delay_fixed
1689   },
1690   {
1691     -1,                                 -1,
1692     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1693     &xx_ei.step_delay_random,           0,
1694     &yy_ei.step_delay_random
1695   },
1696
1697   {
1698     -1,                                 -1,
1699     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1700     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1701     &yy_ei.move_pattern
1702   },
1703   {
1704     -1,                                 -1,
1705     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1706     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1707     &yy_ei.move_direction_initial
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1712     &xx_ei.move_stepsize,               TILEX / 8,
1713     &yy_ei.move_stepsize
1714   },
1715
1716   {
1717     -1,                                 -1,
1718     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1719     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1720     &yy_ei.move_enter_element
1721   },
1722   {
1723     -1,                                 -1,
1724     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1725     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1726     &yy_ei.move_leave_element
1727   },
1728   {
1729     -1,                                 -1,
1730     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1731     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1732     &yy_ei.move_leave_type
1733   },
1734
1735   {
1736     -1,                                 -1,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1738     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1739     &yy_ei.slippery_type
1740   },
1741
1742   {
1743     -1,                                 -1,
1744     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1745     &xx_ei.explosion_type,              EXPLODES_3X3,
1746     &yy_ei.explosion_type
1747   },
1748   {
1749     -1,                                 -1,
1750     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1751     &xx_ei.explosion_delay,             16,
1752     &yy_ei.explosion_delay
1753   },
1754   {
1755     -1,                                 -1,
1756     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1757     &xx_ei.ignition_delay,              8,
1758     &yy_ei.ignition_delay
1759   },
1760
1761   {
1762     -1,                                 -1,
1763     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1764     &xx_ei.content,                     EL_EMPTY_SPACE,
1765     &yy_ei.content,
1766     &xx_num_contents,                   1, 1
1767   },
1768
1769   // ---------- "num_change_pages" must be the last entry ---------------------
1770
1771   {
1772     -1,                                 SAVE_CONF_ALWAYS,
1773     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1774     &xx_ei.num_change_pages,            1,
1775     &yy_ei.num_change_pages
1776   },
1777
1778   {
1779     -1,                                 -1,
1780     -1,                                 -1,
1781     NULL,                               -1,
1782     NULL
1783   }
1784 };
1785
1786 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1787 {
1788   // ---------- "current_change_page" must be the first entry -----------------
1789
1790   {
1791     -1,                                 SAVE_CONF_ALWAYS,
1792     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1793     &xx_current_change_page,            -1
1794   },
1795
1796   // ---------- (the remaining entries can be in any order) -------------------
1797
1798   {
1799     -1,                                 -1,
1800     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1801     &xx_change.can_change,              FALSE
1802   },
1803
1804   {
1805     -1,                                 -1,
1806     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1807     &xx_event_bits[0],                  0
1808   },
1809   {
1810     -1,                                 -1,
1811     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1812     &xx_event_bits[1],                  0
1813   },
1814
1815   {
1816     -1,                                 -1,
1817     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1818     &xx_change.trigger_player,          CH_PLAYER_ANY
1819   },
1820   {
1821     -1,                                 -1,
1822     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1823     &xx_change.trigger_side,            CH_SIDE_ANY
1824   },
1825   {
1826     -1,                                 -1,
1827     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1828     &xx_change.trigger_page,            CH_PAGE_ANY
1829   },
1830
1831   {
1832     -1,                                 -1,
1833     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1834     &xx_change.target_element,          EL_EMPTY_SPACE
1835   },
1836
1837   {
1838     -1,                                 -1,
1839     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1840     &xx_change.delay_fixed,             0
1841   },
1842   {
1843     -1,                                 -1,
1844     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1845     &xx_change.delay_random,            0
1846   },
1847   {
1848     -1,                                 -1,
1849     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1850     &xx_change.delay_frames,            FRAMES_PER_SECOND
1851   },
1852
1853   {
1854     -1,                                 -1,
1855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1856     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1857   },
1858
1859   {
1860     -1,                                 -1,
1861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1862     &xx_change.explode,                 FALSE
1863   },
1864   {
1865     -1,                                 -1,
1866     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1867     &xx_change.use_target_content,      FALSE
1868   },
1869   {
1870     -1,                                 -1,
1871     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1872     &xx_change.only_if_complete,        FALSE
1873   },
1874   {
1875     -1,                                 -1,
1876     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1877     &xx_change.use_random_replace,      FALSE
1878   },
1879   {
1880     -1,                                 -1,
1881     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1882     &xx_change.random_percentage,       100
1883   },
1884   {
1885     -1,                                 -1,
1886     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1887     &xx_change.replace_when,            CP_WHEN_EMPTY
1888   },
1889
1890   {
1891     -1,                                 -1,
1892     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1893     &xx_change.has_action,              FALSE
1894   },
1895   {
1896     -1,                                 -1,
1897     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1898     &xx_change.action_type,             CA_NO_ACTION
1899   },
1900   {
1901     -1,                                 -1,
1902     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1903     &xx_change.action_mode,             CA_MODE_UNDEFINED
1904   },
1905   {
1906     -1,                                 -1,
1907     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1908     &xx_change.action_arg,              CA_ARG_UNDEFINED
1909   },
1910
1911   {
1912     -1,                                 -1,
1913     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1914     &xx_change.action_element,          EL_EMPTY_SPACE
1915   },
1916
1917   {
1918     -1,                                 -1,
1919     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1920     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1921     &xx_num_contents,                   1, 1
1922   },
1923
1924   {
1925     -1,                                 -1,
1926     -1,                                 -1,
1927     NULL,                               -1
1928   }
1929 };
1930
1931 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1932 {
1933   {
1934     -1,                                 -1,
1935     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1936     &xx_ei.description[0],              -1, NULL,
1937     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1938     &xx_default_description[0]
1939   },
1940
1941   {
1942     -1,                                 -1,
1943     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1944     &xx_ei.use_gfx_element,             FALSE
1945   },
1946   {
1947     -1,                                 -1,
1948     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1949     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1950   },
1951
1952   {
1953     -1,                                 -1,
1954     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1955     &xx_group.choice_mode,              ANIM_RANDOM
1956   },
1957
1958   {
1959     -1,                                 -1,
1960     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1961     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1962     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1963   },
1964
1965   {
1966     -1,                                 -1,
1967     -1,                                 -1,
1968     NULL,                               -1
1969   }
1970 };
1971
1972 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1973 {
1974   {
1975     -1,                                 -1,
1976     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1977     &xx_ei.use_gfx_element,             FALSE
1978   },
1979   {
1980     -1,                                 -1,
1981     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1982     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1983   },
1984
1985   {
1986     -1,                                 -1,
1987     -1,                                 -1,
1988     NULL,                               -1
1989   }
1990 };
1991
1992 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1993 {
1994   {
1995     EL_PLAYER_1,                        -1,
1996     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1997     &li.block_snap_field,               TRUE
1998   },
1999   {
2000     EL_PLAYER_1,                        -1,
2001     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
2002     &li.continuous_snapping,            TRUE
2003   },
2004   {
2005     EL_PLAYER_1,                        -1,
2006     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
2007     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
2008   },
2009   {
2010     EL_PLAYER_1,                        -1,
2011     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
2012     &li.use_start_element[0],           FALSE
2013   },
2014   {
2015     EL_PLAYER_1,                        -1,
2016     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
2017     &li.start_element[0],               EL_PLAYER_1
2018   },
2019   {
2020     EL_PLAYER_1,                        -1,
2021     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
2022     &li.use_artwork_element[0],         FALSE
2023   },
2024   {
2025     EL_PLAYER_1,                        -1,
2026     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
2027     &li.artwork_element[0],             EL_PLAYER_1
2028   },
2029   {
2030     EL_PLAYER_1,                        -1,
2031     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
2032     &li.use_explosion_element[0],       FALSE
2033   },
2034   {
2035     EL_PLAYER_1,                        -1,
2036     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
2037     &li.explosion_element[0],           EL_PLAYER_1
2038   },
2039
2040   {
2041     -1,                                 -1,
2042     -1,                                 -1,
2043     NULL,                               -1
2044   }
2045 };
2046
2047 static struct
2048 {
2049   int filetype;
2050   char *id;
2051 }
2052 filetype_id_list[] =
2053 {
2054   { LEVEL_FILE_TYPE_RND,        "RND"   },
2055   { LEVEL_FILE_TYPE_BD,         "BD"    },
2056   { LEVEL_FILE_TYPE_EM,         "EM"    },
2057   { LEVEL_FILE_TYPE_SP,         "SP"    },
2058   { LEVEL_FILE_TYPE_DX,         "DX"    },
2059   { LEVEL_FILE_TYPE_SB,         "SB"    },
2060   { LEVEL_FILE_TYPE_DC,         "DC"    },
2061   { LEVEL_FILE_TYPE_MM,         "MM"    },
2062   { LEVEL_FILE_TYPE_MM,         "DF"    },
2063   { -1,                         NULL    },
2064 };
2065
2066
2067 // ============================================================================
2068 // level file functions
2069 // ============================================================================
2070
2071 static boolean check_special_flags(char *flag)
2072 {
2073   if (strEqual(options.special_flags, flag) ||
2074       strEqual(leveldir_current->special_flags, flag))
2075     return TRUE;
2076
2077   return FALSE;
2078 }
2079
2080 static struct DateInfo getCurrentDate(void)
2081 {
2082   time_t epoch_seconds = time(NULL);
2083   struct tm *now = localtime(&epoch_seconds);
2084   struct DateInfo date;
2085
2086   date.year  = now->tm_year + 1900;
2087   date.month = now->tm_mon  + 1;
2088   date.day   = now->tm_mday;
2089
2090   date.src   = DATE_SRC_CLOCK;
2091
2092   return date;
2093 }
2094
2095 static void resetEventFlags(struct ElementChangeInfo *change)
2096 {
2097   int i;
2098
2099   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2100     change->has_event[i] = FALSE;
2101 }
2102
2103 static void resetEventBits(void)
2104 {
2105   int i;
2106
2107   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2108     xx_event_bits[i] = 0;
2109 }
2110
2111 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2112 {
2113   int i;
2114
2115   /* important: only change event flag if corresponding event bit is set
2116      (this is because all xx_event_bits[] values are loaded separately,
2117      and all xx_event_bits[] values are set back to zero before loading
2118      another value xx_event_bits[x] (each value representing 32 flags)) */
2119
2120   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2121     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2122       change->has_event[i] = TRUE;
2123 }
2124
2125 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2126 {
2127   int i;
2128
2129   /* in contrast to the above function setEventFlagsFromEventBits(), it
2130      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2131      depending on the corresponding change->has_event[i] values here, as
2132      all xx_event_bits[] values are reset in resetEventBits() before */
2133
2134   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2135     if (change->has_event[i])
2136       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2137 }
2138
2139 static char *getDefaultElementDescription(struct ElementInfo *ei)
2140 {
2141   static char description[MAX_ELEMENT_NAME_LEN + 1];
2142   char *default_description = (ei->custom_description != NULL ?
2143                                ei->custom_description :
2144                                ei->editor_description);
2145   int i;
2146
2147   // always start with reliable default values
2148   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2149     description[i] = '\0';
2150
2151   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2152   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2153
2154   return &description[0];
2155 }
2156
2157 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2158 {
2159   char *default_description = getDefaultElementDescription(ei);
2160   int i;
2161
2162   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2163     ei->description[i] = default_description[i];
2164 }
2165
2166 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2167 {
2168   int i;
2169
2170   for (i = 0; conf[i].data_type != -1; i++)
2171   {
2172     int default_value = conf[i].default_value;
2173     int data_type = conf[i].data_type;
2174     int conf_type = conf[i].conf_type;
2175     int byte_mask = conf_type & CONF_MASK_BYTES;
2176
2177     if (byte_mask == CONF_MASK_MULTI_BYTES)
2178     {
2179       int default_num_entities = conf[i].default_num_entities;
2180       int max_num_entities = conf[i].max_num_entities;
2181
2182       *(int *)(conf[i].num_entities) = default_num_entities;
2183
2184       if (data_type == TYPE_STRING)
2185       {
2186         char *default_string = conf[i].default_string;
2187         char *string = (char *)(conf[i].value);
2188
2189         strncpy(string, default_string, max_num_entities);
2190       }
2191       else if (data_type == TYPE_ELEMENT_LIST)
2192       {
2193         int *element_array = (int *)(conf[i].value);
2194         int j;
2195
2196         for (j = 0; j < max_num_entities; j++)
2197           element_array[j] = default_value;
2198       }
2199       else if (data_type == TYPE_CONTENT_LIST)
2200       {
2201         struct Content *content = (struct Content *)(conf[i].value);
2202         int c, x, y;
2203
2204         for (c = 0; c < max_num_entities; c++)
2205           for (y = 0; y < 3; y++)
2206             for (x = 0; x < 3; x++)
2207               content[c].e[x][y] = default_value;
2208       }
2209     }
2210     else        // constant size configuration data (1, 2 or 4 bytes)
2211     {
2212       if (data_type == TYPE_BOOLEAN)
2213         *(boolean *)(conf[i].value) = default_value;
2214       else
2215         *(int *)    (conf[i].value) = default_value;
2216     }
2217   }
2218 }
2219
2220 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2221 {
2222   int i;
2223
2224   for (i = 0; conf[i].data_type != -1; i++)
2225   {
2226     int data_type = conf[i].data_type;
2227     int conf_type = conf[i].conf_type;
2228     int byte_mask = conf_type & CONF_MASK_BYTES;
2229
2230     if (byte_mask == CONF_MASK_MULTI_BYTES)
2231     {
2232       int max_num_entities = conf[i].max_num_entities;
2233
2234       if (data_type == TYPE_STRING)
2235       {
2236         char *string      = (char *)(conf[i].value);
2237         char *string_copy = (char *)(conf[i].value_copy);
2238
2239         strncpy(string_copy, string, max_num_entities);
2240       }
2241       else if (data_type == TYPE_ELEMENT_LIST)
2242       {
2243         int *element_array      = (int *)(conf[i].value);
2244         int *element_array_copy = (int *)(conf[i].value_copy);
2245         int j;
2246
2247         for (j = 0; j < max_num_entities; j++)
2248           element_array_copy[j] = element_array[j];
2249       }
2250       else if (data_type == TYPE_CONTENT_LIST)
2251       {
2252         struct Content *content      = (struct Content *)(conf[i].value);
2253         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2254         int c, x, y;
2255
2256         for (c = 0; c < max_num_entities; c++)
2257           for (y = 0; y < 3; y++)
2258             for (x = 0; x < 3; x++)
2259               content_copy[c].e[x][y] = content[c].e[x][y];
2260       }
2261     }
2262     else        // constant size configuration data (1, 2 or 4 bytes)
2263     {
2264       if (data_type == TYPE_BOOLEAN)
2265         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2266       else
2267         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2268     }
2269   }
2270 }
2271
2272 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2273 {
2274   int i;
2275
2276   xx_ei = *ei_from;     // copy element data into temporary buffer
2277   yy_ei = *ei_to;       // copy element data into temporary buffer
2278
2279   copyConfigFromConfigList(chunk_config_CUSX_base);
2280
2281   *ei_from = xx_ei;
2282   *ei_to   = yy_ei;
2283
2284   // ---------- reinitialize and copy change pages ----------
2285
2286   ei_to->num_change_pages = ei_from->num_change_pages;
2287   ei_to->current_change_page = ei_from->current_change_page;
2288
2289   setElementChangePages(ei_to, ei_to->num_change_pages);
2290
2291   for (i = 0; i < ei_to->num_change_pages; i++)
2292     ei_to->change_page[i] = ei_from->change_page[i];
2293
2294   // ---------- copy group element info ----------
2295   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2296     *ei_to->group = *ei_from->group;
2297
2298   // mark this custom element as modified
2299   ei_to->modified_settings = TRUE;
2300 }
2301
2302 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2303 {
2304   int change_page_size = sizeof(struct ElementChangeInfo);
2305
2306   ei->num_change_pages = MAX(1, change_pages);
2307
2308   ei->change_page =
2309     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2310
2311   if (ei->current_change_page >= ei->num_change_pages)
2312     ei->current_change_page = ei->num_change_pages - 1;
2313
2314   ei->change = &ei->change_page[ei->current_change_page];
2315 }
2316
2317 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2318 {
2319   xx_change = *change;          // copy change data into temporary buffer
2320
2321   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2322
2323   *change = xx_change;
2324
2325   resetEventFlags(change);
2326
2327   change->direct_action = 0;
2328   change->other_action = 0;
2329
2330   change->pre_change_function = NULL;
2331   change->change_function = NULL;
2332   change->post_change_function = NULL;
2333 }
2334
2335 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2336 {
2337   int i, x, y;
2338
2339   li = *level;          // copy level data into temporary buffer
2340   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2341   *level = li;          // copy temporary buffer back to level data
2342
2343   setLevelInfoToDefaults_BD();
2344   setLevelInfoToDefaults_EM();
2345   setLevelInfoToDefaults_SP();
2346   setLevelInfoToDefaults_MM();
2347
2348   level->native_bd_level = &native_bd_level;
2349   level->native_em_level = &native_em_level;
2350   level->native_sp_level = &native_sp_level;
2351   level->native_mm_level = &native_mm_level;
2352
2353   level->file_version = FILE_VERSION_ACTUAL;
2354   level->game_version = GAME_VERSION_ACTUAL;
2355
2356   level->creation_date = getCurrentDate();
2357
2358   level->encoding_16bit_field  = TRUE;
2359   level->encoding_16bit_yamyam = TRUE;
2360   level->encoding_16bit_amoeba = TRUE;
2361
2362   // clear level name and level author string buffers
2363   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2364     level->name[i] = '\0';
2365   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2366     level->author[i] = '\0';
2367
2368   // set level name and level author to default values
2369   strcpy(level->name, NAMELESS_LEVEL_NAME);
2370   strcpy(level->author, ANONYMOUS_NAME);
2371
2372   // set default game engine type
2373   level->game_engine_type = setup.default_game_engine_type;
2374
2375   // set level playfield to playable default level with player and exit
2376   for (x = 0; x < MAX_LEV_FIELDX; x++)
2377     for (y = 0; y < MAX_LEV_FIELDY; y++)
2378       level->field[x][y] = EL_SAND;
2379
2380   level->field[0][0] = EL_PLAYER_1;
2381   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2382
2383   BorderElement = EL_STEELWALL;
2384
2385   // detect custom elements when loading them
2386   level->file_has_custom_elements = FALSE;
2387
2388   // set random colors for BD style levels according to preferred color type
2389   SetRandomLevelColors_BD(setup.bd_default_color_type);
2390
2391   // set default color type and colors for BD style level colors
2392   SetDefaultLevelColorType_BD();
2393   SetDefaultLevelColors_BD();
2394
2395   // set all bug compatibility flags to "false" => do not emulate this bug
2396   level->use_action_after_change_bug = FALSE;
2397
2398   if (leveldir_current)
2399   {
2400     // try to determine better author name than 'anonymous'
2401     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2402     {
2403       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2404       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2405     }
2406     else
2407     {
2408       switch (LEVELCLASS(leveldir_current))
2409       {
2410         case LEVELCLASS_TUTORIAL:
2411           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2412           break;
2413
2414         case LEVELCLASS_CONTRIB:
2415           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2416           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2417           break;
2418
2419         case LEVELCLASS_PRIVATE:
2420           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2421           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2422           break;
2423
2424         default:
2425           // keep default value
2426           break;
2427       }
2428     }
2429   }
2430 }
2431
2432 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2433 {
2434   static boolean clipboard_elements_initialized = FALSE;
2435   int i;
2436
2437   InitElementPropertiesStatic();
2438
2439   li = *level;          // copy level data into temporary buffer
2440   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2441   *level = li;          // copy temporary buffer back to level data
2442
2443   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2444   {
2445     int element = i;
2446     struct ElementInfo *ei = &element_info[element];
2447
2448     if (element == EL_MM_GRAY_BALL)
2449     {
2450       struct LevelInfo_MM *level_mm = level->native_mm_level;
2451       int j;
2452
2453       for (j = 0; j < level->num_mm_ball_contents; j++)
2454         level->mm_ball_content[j] =
2455           map_element_MM_to_RND(level_mm->ball_content[j]);
2456     }
2457
2458     // never initialize clipboard elements after the very first time
2459     // (to be able to use clipboard elements between several levels)
2460     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2461       continue;
2462
2463     if (IS_ENVELOPE(element))
2464     {
2465       int envelope_nr = element - EL_ENVELOPE_1;
2466
2467       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2468
2469       level->envelope[envelope_nr] = xx_envelope;
2470     }
2471
2472     if (IS_CUSTOM_ELEMENT(element) ||
2473         IS_GROUP_ELEMENT(element) ||
2474         IS_INTERNAL_ELEMENT(element))
2475     {
2476       xx_ei = *ei;      // copy element data into temporary buffer
2477
2478       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2479
2480       *ei = xx_ei;
2481     }
2482
2483     setElementChangePages(ei, 1);
2484     setElementChangeInfoToDefaults(ei->change);
2485
2486     if (IS_CUSTOM_ELEMENT(element) ||
2487         IS_GROUP_ELEMENT(element))
2488     {
2489       setElementDescriptionToDefault(ei);
2490
2491       ei->modified_settings = FALSE;
2492     }
2493
2494     if (IS_CUSTOM_ELEMENT(element) ||
2495         IS_INTERNAL_ELEMENT(element))
2496     {
2497       // internal values used in level editor
2498
2499       ei->access_type = 0;
2500       ei->access_layer = 0;
2501       ei->access_protected = 0;
2502       ei->walk_to_action = 0;
2503       ei->smash_targets = 0;
2504       ei->deadliness = 0;
2505
2506       ei->can_explode_by_fire = FALSE;
2507       ei->can_explode_smashed = FALSE;
2508       ei->can_explode_impact = FALSE;
2509
2510       ei->current_change_page = 0;
2511     }
2512
2513     if (IS_GROUP_ELEMENT(element) ||
2514         IS_INTERNAL_ELEMENT(element))
2515     {
2516       struct ElementGroupInfo *group;
2517
2518       // initialize memory for list of elements in group
2519       if (ei->group == NULL)
2520         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2521
2522       group = ei->group;
2523
2524       xx_group = *group;        // copy group data into temporary buffer
2525
2526       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2527
2528       *group = xx_group;
2529     }
2530
2531     if (IS_EMPTY_ELEMENT(element) ||
2532         IS_INTERNAL_ELEMENT(element))
2533     {
2534       xx_ei = *ei;              // copy element data into temporary buffer
2535
2536       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2537
2538       *ei = xx_ei;
2539     }
2540   }
2541
2542   clipboard_elements_initialized = TRUE;
2543 }
2544
2545 static void setLevelInfoToDefaults(struct LevelInfo *level,
2546                                    boolean level_info_only,
2547                                    boolean reset_file_status)
2548 {
2549   setLevelInfoToDefaults_Level(level);
2550
2551   if (!level_info_only)
2552     setLevelInfoToDefaults_Elements(level);
2553
2554   if (reset_file_status)
2555   {
2556     level->no_valid_file = FALSE;
2557     level->no_level_file = FALSE;
2558   }
2559
2560   level->changed = FALSE;
2561 }
2562
2563 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2564 {
2565   level_file_info->nr = 0;
2566   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2567   level_file_info->packed = FALSE;
2568
2569   setString(&level_file_info->basename, NULL);
2570   setString(&level_file_info->filename, NULL);
2571 }
2572
2573 int getMappedElement_SB(int, boolean);
2574
2575 static void ActivateLevelTemplate(void)
2576 {
2577   int x, y;
2578
2579   if (check_special_flags("load_xsb_to_ces"))
2580   {
2581     // fill smaller playfields with padding "beyond border wall" elements
2582     if (level.fieldx < level_template.fieldx ||
2583         level.fieldy < level_template.fieldy)
2584     {
2585       short field[level.fieldx][level.fieldy];
2586       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2587       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2588       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2589       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2590
2591       // copy old playfield (which is smaller than the visible area)
2592       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2593         field[x][y] = level.field[x][y];
2594
2595       // fill new, larger playfield with "beyond border wall" elements
2596       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2597         level.field[x][y] = getMappedElement_SB('_', TRUE);
2598
2599       // copy the old playfield to the middle of the new playfield
2600       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2601         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2602
2603       level.fieldx = new_fieldx;
2604       level.fieldy = new_fieldy;
2605     }
2606   }
2607
2608   // Currently there is no special action needed to activate the template
2609   // data, because 'element_info' property settings overwrite the original
2610   // level data, while all other variables do not change.
2611
2612   // Exception: 'from_level_template' elements in the original level playfield
2613   // are overwritten with the corresponding elements at the same position in
2614   // playfield from the level template.
2615
2616   for (x = 0; x < level.fieldx; x++)
2617     for (y = 0; y < level.fieldy; y++)
2618       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2619         level.field[x][y] = level_template.field[x][y];
2620
2621   if (check_special_flags("load_xsb_to_ces"))
2622   {
2623     struct LevelInfo level_backup = level;
2624
2625     // overwrite all individual level settings from template level settings
2626     level = level_template;
2627
2628     // restore level file info
2629     level.file_info = level_backup.file_info;
2630
2631     // restore playfield size
2632     level.fieldx = level_backup.fieldx;
2633     level.fieldy = level_backup.fieldy;
2634
2635     // restore playfield content
2636     for (x = 0; x < level.fieldx; x++)
2637       for (y = 0; y < level.fieldy; y++)
2638         level.field[x][y] = level_backup.field[x][y];
2639
2640     // restore name and author from individual level
2641     strcpy(level.name,   level_backup.name);
2642     strcpy(level.author, level_backup.author);
2643
2644     // restore flag "use_custom_template"
2645     level.use_custom_template = level_backup.use_custom_template;
2646   }
2647 }
2648
2649 static boolean checkForPackageFromBasename_BD(char *basename)
2650 {
2651   // check for native BD level file extensions
2652   if (!strSuffixLower(basename, ".bd") &&
2653       !strSuffixLower(basename, ".bdr") &&
2654       !strSuffixLower(basename, ".brc") &&
2655       !strSuffixLower(basename, ".gds"))
2656     return FALSE;
2657
2658   // check for standard single-level BD files (like "001.bd")
2659   if (strSuffixLower(basename, ".bd") &&
2660       strlen(basename) == 6 &&
2661       basename[0] >= '0' && basename[0] <= '9' &&
2662       basename[1] >= '0' && basename[1] <= '9' &&
2663       basename[2] >= '0' && basename[2] <= '9')
2664     return FALSE;
2665
2666   // this is a level package in native BD file format
2667   return TRUE;
2668 }
2669
2670 static char *getLevelFilenameFromBasename(char *basename)
2671 {
2672   static char *filename = NULL;
2673
2674   checked_free(filename);
2675
2676   filename = getPath2(getCurrentLevelDir(), basename);
2677
2678   return filename;
2679 }
2680
2681 static int getFileTypeFromBasename(char *basename)
2682 {
2683   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2684
2685   static char *filename = NULL;
2686   struct stat file_status;
2687
2688   // ---------- try to determine file type from filename ----------
2689
2690   // check for typical filename of a Supaplex level package file
2691   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2692     return LEVEL_FILE_TYPE_SP;
2693
2694   // check for typical filename of a Diamond Caves II level package file
2695   if (strSuffixLower(basename, ".dc") ||
2696       strSuffixLower(basename, ".dc2"))
2697     return LEVEL_FILE_TYPE_DC;
2698
2699   // check for typical filename of a Sokoban level package file
2700   if (strSuffixLower(basename, ".xsb") &&
2701       strchr(basename, '%') == NULL)
2702     return LEVEL_FILE_TYPE_SB;
2703
2704   // check for typical filename of a Boulder Dash (GDash) level package file
2705   if (checkForPackageFromBasename_BD(basename))
2706     return LEVEL_FILE_TYPE_BD;
2707
2708   // ---------- try to determine file type from filesize ----------
2709
2710   checked_free(filename);
2711   filename = getPath2(getCurrentLevelDir(), basename);
2712
2713   if (stat(filename, &file_status) == 0)
2714   {
2715     // check for typical filesize of a Supaplex level package file
2716     if (file_status.st_size == 170496)
2717       return LEVEL_FILE_TYPE_SP;
2718   }
2719
2720   return LEVEL_FILE_TYPE_UNKNOWN;
2721 }
2722
2723 static int getFileTypeFromMagicBytes(char *filename, int type)
2724 {
2725   File *file;
2726
2727   if ((file = openFile(filename, MODE_READ)))
2728   {
2729     char chunk_name[CHUNK_ID_LEN + 1];
2730
2731     getFileChunkBE(file, chunk_name, NULL);
2732
2733     if (strEqual(chunk_name, "MMII") ||
2734         strEqual(chunk_name, "MIRR"))
2735       type = LEVEL_FILE_TYPE_MM;
2736
2737     closeFile(file);
2738   }
2739
2740   return type;
2741 }
2742
2743 static boolean checkForPackageFromBasename(char *basename)
2744 {
2745   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2746   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2747
2748   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2749 }
2750
2751 static char *getSingleLevelBasenameExt(int nr, char *extension)
2752 {
2753   static char basename[MAX_FILENAME_LEN];
2754
2755   if (nr < 0)
2756     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2757   else
2758     sprintf(basename, "%03d.%s", nr, extension);
2759
2760   return basename;
2761 }
2762
2763 static char *getSingleLevelBasename(int nr)
2764 {
2765   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2766 }
2767
2768 static char *getPackedLevelBasename(int type)
2769 {
2770   static char basename[MAX_FILENAME_LEN];
2771   char *directory = getCurrentLevelDir();
2772   Directory *dir;
2773   DirectoryEntry *dir_entry;
2774
2775   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2776
2777   if ((dir = openDirectory(directory)) == NULL)
2778   {
2779     Warn("cannot read current level directory '%s'", directory);
2780
2781     return basename;
2782   }
2783
2784   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2785   {
2786     char *entry_basename = dir_entry->basename;
2787     int entry_type = getFileTypeFromBasename(entry_basename);
2788
2789     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2790     {
2791       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2792           type == entry_type)
2793       {
2794         strcpy(basename, entry_basename);
2795
2796         break;
2797       }
2798     }
2799   }
2800
2801   closeDirectory(dir);
2802
2803   return basename;
2804 }
2805
2806 static char *getSingleLevelFilename(int nr)
2807 {
2808   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2809 }
2810
2811 #if ENABLE_UNUSED_CODE
2812 static char *getPackedLevelFilename(int type)
2813 {
2814   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2815 }
2816 #endif
2817
2818 char *getDefaultLevelFilename(int nr)
2819 {
2820   return getSingleLevelFilename(nr);
2821 }
2822
2823 #if ENABLE_UNUSED_CODE
2824 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2825                                                  int type)
2826 {
2827   lfi->type = type;
2828   lfi->packed = FALSE;
2829
2830   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2831   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2832 }
2833 #endif
2834
2835 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2836                                                  int type, char *format, ...)
2837 {
2838   static char basename[MAX_FILENAME_LEN];
2839   va_list ap;
2840
2841   va_start(ap, format);
2842   vsprintf(basename, format, ap);
2843   va_end(ap);
2844
2845   lfi->type = type;
2846   lfi->packed = FALSE;
2847
2848   setString(&lfi->basename, basename);
2849   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2850 }
2851
2852 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2853                                                  int type)
2854 {
2855   lfi->type = type;
2856   lfi->packed = TRUE;
2857
2858   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2859   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2860 }
2861
2862 static int getFiletypeFromID(char *filetype_id)
2863 {
2864   char *filetype_id_lower;
2865   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2866   int i;
2867
2868   if (filetype_id == NULL)
2869     return LEVEL_FILE_TYPE_UNKNOWN;
2870
2871   filetype_id_lower = getStringToLower(filetype_id);
2872
2873   for (i = 0; filetype_id_list[i].id != NULL; i++)
2874   {
2875     char *id_lower = getStringToLower(filetype_id_list[i].id);
2876     
2877     if (strEqual(filetype_id_lower, id_lower))
2878       filetype = filetype_id_list[i].filetype;
2879
2880     free(id_lower);
2881
2882     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2883       break;
2884   }
2885
2886   free(filetype_id_lower);
2887
2888   return filetype;
2889 }
2890
2891 char *getLocalLevelTemplateFilename(void)
2892 {
2893   return getDefaultLevelFilename(-1);
2894 }
2895
2896 char *getGlobalLevelTemplateFilename(void)
2897 {
2898   // global variable "leveldir_current" must be modified in the loop below
2899   LevelDirTree *leveldir_current_last = leveldir_current;
2900   char *filename = NULL;
2901
2902   // check for template level in path from current to topmost tree node
2903
2904   while (leveldir_current != NULL)
2905   {
2906     filename = getDefaultLevelFilename(-1);
2907
2908     if (fileExists(filename))
2909       break;
2910
2911     leveldir_current = leveldir_current->node_parent;
2912   }
2913
2914   // restore global variable "leveldir_current" modified in above loop
2915   leveldir_current = leveldir_current_last;
2916
2917   return filename;
2918 }
2919
2920 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2921 {
2922   int nr = lfi->nr;
2923
2924   // special case: level number is negative => check for level template file
2925   if (nr < 0)
2926   {
2927     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2928                                          getSingleLevelBasename(-1));
2929
2930     // replace local level template filename with global template filename
2931     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2932
2933     // no fallback if template file not existing
2934     return;
2935   }
2936
2937   // special case: check for file name/pattern specified in "levelinfo.conf"
2938   if (leveldir_current->level_filename != NULL)
2939   {
2940     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2941
2942     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2943                                          leveldir_current->level_filename, nr);
2944
2945     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2946
2947     if (fileExists(lfi->filename))
2948       return;
2949   }
2950   else if (leveldir_current->level_filetype != NULL)
2951   {
2952     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2953
2954     // check for specified native level file with standard file name
2955     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2956                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2957     if (fileExists(lfi->filename))
2958       return;
2959   }
2960
2961   // check for native Rocks'n'Diamonds level file
2962   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2963                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2964   if (fileExists(lfi->filename))
2965     return;
2966
2967   // check for native Boulder Dash level file
2968   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2969   if (fileExists(lfi->filename))
2970     return;
2971
2972   // check for Emerald Mine level file (V1)
2973   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2974                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2975   if (fileExists(lfi->filename))
2976     return;
2977   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2978                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2979   if (fileExists(lfi->filename))
2980     return;
2981
2982   // check for Emerald Mine level file (V2 to V5)
2983   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2984   if (fileExists(lfi->filename))
2985     return;
2986
2987   // check for Emerald Mine level file (V6 / single mode)
2988   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2989   if (fileExists(lfi->filename))
2990     return;
2991   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2992   if (fileExists(lfi->filename))
2993     return;
2994
2995   // check for Emerald Mine level file (V6 / teamwork mode)
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2997   if (fileExists(lfi->filename))
2998     return;
2999   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
3000   if (fileExists(lfi->filename))
3001     return;
3002
3003   // check for various packed level file formats
3004   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3005   if (fileExists(lfi->filename))
3006     return;
3007
3008   // no known level file found -- use default values (and fail later)
3009   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3010                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3011 }
3012
3013 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3014 {
3015   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3016     lfi->type = getFileTypeFromBasename(lfi->basename);
3017
3018   if (lfi->type == LEVEL_FILE_TYPE_RND)
3019     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3020 }
3021
3022 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3023 {
3024   // always start with reliable default values
3025   setFileInfoToDefaults(level_file_info);
3026
3027   level_file_info->nr = nr;     // set requested level number
3028
3029   determineLevelFileInfo_Filename(level_file_info);
3030   determineLevelFileInfo_Filetype(level_file_info);
3031 }
3032
3033 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3034                               struct LevelFileInfo *lfi_to)
3035 {
3036   lfi_to->nr     = lfi_from->nr;
3037   lfi_to->type   = lfi_from->type;
3038   lfi_to->packed = lfi_from->packed;
3039
3040   setString(&lfi_to->basename, lfi_from->basename);
3041   setString(&lfi_to->filename, lfi_from->filename);
3042 }
3043
3044 // ----------------------------------------------------------------------------
3045 // functions for loading R'n'D level
3046 // ----------------------------------------------------------------------------
3047
3048 int getMappedElement(int element)
3049 {
3050   // remap some (historic, now obsolete) elements
3051
3052   switch (element)
3053   {
3054     case EL_PLAYER_OBSOLETE:
3055       element = EL_PLAYER_1;
3056       break;
3057
3058     case EL_KEY_OBSOLETE:
3059       element = EL_KEY_1;
3060       break;
3061
3062     case EL_EM_KEY_1_FILE_OBSOLETE:
3063       element = EL_EM_KEY_1;
3064       break;
3065
3066     case EL_EM_KEY_2_FILE_OBSOLETE:
3067       element = EL_EM_KEY_2;
3068       break;
3069
3070     case EL_EM_KEY_3_FILE_OBSOLETE:
3071       element = EL_EM_KEY_3;
3072       break;
3073
3074     case EL_EM_KEY_4_FILE_OBSOLETE:
3075       element = EL_EM_KEY_4;
3076       break;
3077
3078     case EL_ENVELOPE_OBSOLETE:
3079       element = EL_ENVELOPE_1;
3080       break;
3081
3082     case EL_SP_EMPTY:
3083       element = EL_EMPTY;
3084       break;
3085
3086     default:
3087       if (element >= NUM_FILE_ELEMENTS)
3088       {
3089         Warn("invalid level element %d", element);
3090
3091         element = EL_UNKNOWN;
3092       }
3093       break;
3094   }
3095
3096   return element;
3097 }
3098
3099 static int getMappedElementByVersion(int element, int game_version)
3100 {
3101   // remap some elements due to certain game version
3102
3103   if (game_version <= VERSION_IDENT(2,2,0,0))
3104   {
3105     // map game font elements
3106     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3107                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3108                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3109                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3110   }
3111
3112   if (game_version < VERSION_IDENT(3,0,0,0))
3113   {
3114     // map Supaplex gravity tube elements
3115     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3116                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3117                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3118                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3119                element);
3120   }
3121
3122   return element;
3123 }
3124
3125 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3126 {
3127   level->file_version = getFileVersion(file);
3128   level->game_version = getFileVersion(file);
3129
3130   return chunk_size;
3131 }
3132
3133 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3134 {
3135   level->creation_date.year  = getFile16BitBE(file);
3136   level->creation_date.month = getFile8Bit(file);
3137   level->creation_date.day   = getFile8Bit(file);
3138
3139   level->creation_date.src   = DATE_SRC_LEVELFILE;
3140
3141   return chunk_size;
3142 }
3143
3144 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3145 {
3146   int initial_player_stepsize;
3147   int initial_player_gravity;
3148   int i, x, y;
3149
3150   level->fieldx = getFile8Bit(file);
3151   level->fieldy = getFile8Bit(file);
3152
3153   level->time           = getFile16BitBE(file);
3154   level->gems_needed    = getFile16BitBE(file);
3155
3156   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3157     level->name[i] = getFile8Bit(file);
3158   level->name[MAX_LEVEL_NAME_LEN] = 0;
3159
3160   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3161     level->score[i] = getFile8Bit(file);
3162
3163   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3164   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3165     for (y = 0; y < 3; y++)
3166       for (x = 0; x < 3; x++)
3167         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3168
3169   level->amoeba_speed           = getFile8Bit(file);
3170   level->time_magic_wall        = getFile8Bit(file);
3171   level->time_wheel             = getFile8Bit(file);
3172   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3173
3174   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3175                                    STEPSIZE_NORMAL);
3176
3177   for (i = 0; i < MAX_PLAYERS; i++)
3178     level->initial_player_stepsize[i] = initial_player_stepsize;
3179
3180   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3181
3182   for (i = 0; i < MAX_PLAYERS; i++)
3183     level->initial_player_gravity[i] = initial_player_gravity;
3184
3185   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3186   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3187
3188   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3189
3190   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3191   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3192   level->can_move_into_acid_bits = getFile32BitBE(file);
3193   level->dont_collide_with_bits = getFile8Bit(file);
3194
3195   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3197
3198   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3199   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3200   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3201
3202   level->game_engine_type       = getFile8Bit(file);
3203
3204   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3205
3206   return chunk_size;
3207 }
3208
3209 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3210 {
3211   int i;
3212
3213   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3214     level->name[i] = getFile8Bit(file);
3215   level->name[MAX_LEVEL_NAME_LEN] = 0;
3216
3217   return chunk_size;
3218 }
3219
3220 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3221 {
3222   int i;
3223
3224   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3225     level->author[i] = getFile8Bit(file);
3226   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3227
3228   return chunk_size;
3229 }
3230
3231 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3232 {
3233   int x, y;
3234   int chunk_size_expected = level->fieldx * level->fieldy;
3235
3236   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3237      stored with 16-bit encoding (and should be twice as big then).
3238      Even worse, playfield data was stored 16-bit when only yamyam content
3239      contained 16-bit elements and vice versa. */
3240
3241   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3242     chunk_size_expected *= 2;
3243
3244   if (chunk_size_expected != chunk_size)
3245   {
3246     ReadUnusedBytesFromFile(file, chunk_size);
3247     return chunk_size_expected;
3248   }
3249
3250   for (y = 0; y < level->fieldy; y++)
3251     for (x = 0; x < level->fieldx; x++)
3252       level->field[x][y] =
3253         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3254                          getFile8Bit(file));
3255   return chunk_size;
3256 }
3257
3258 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3259 {
3260   int i, x, y;
3261   int header_size = 4;
3262   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3263   int chunk_size_expected = header_size + content_size;
3264
3265   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3266      stored with 16-bit encoding (and should be twice as big then).
3267      Even worse, playfield data was stored 16-bit when only yamyam content
3268      contained 16-bit elements and vice versa. */
3269
3270   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3271     chunk_size_expected += content_size;
3272
3273   if (chunk_size_expected != chunk_size)
3274   {
3275     ReadUnusedBytesFromFile(file, chunk_size);
3276     return chunk_size_expected;
3277   }
3278
3279   getFile8Bit(file);
3280   level->num_yamyam_contents = getFile8Bit(file);
3281   getFile8Bit(file);
3282   getFile8Bit(file);
3283
3284   // correct invalid number of content fields -- should never happen
3285   if (level->num_yamyam_contents < 1 ||
3286       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3287     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3288
3289   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3290     for (y = 0; y < 3; y++)
3291       for (x = 0; x < 3; x++)
3292         level->yamyam_content[i].e[x][y] =
3293           getMappedElement(level->encoding_16bit_field ?
3294                            getFile16BitBE(file) : getFile8Bit(file));
3295   return chunk_size;
3296 }
3297
3298 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3299 {
3300   int i, x, y;
3301   int element;
3302   int num_contents;
3303   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3304
3305   element = getMappedElement(getFile16BitBE(file));
3306   num_contents = getFile8Bit(file);
3307
3308   getFile8Bit(file);    // content x size (unused)
3309   getFile8Bit(file);    // content y size (unused)
3310
3311   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3312
3313   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3314     for (y = 0; y < 3; y++)
3315       for (x = 0; x < 3; x++)
3316         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3317
3318   // correct invalid number of content fields -- should never happen
3319   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3320     num_contents = STD_ELEMENT_CONTENTS;
3321
3322   if (element == EL_YAMYAM)
3323   {
3324     level->num_yamyam_contents = num_contents;
3325
3326     for (i = 0; i < num_contents; i++)
3327       for (y = 0; y < 3; y++)
3328         for (x = 0; x < 3; x++)
3329           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3330   }
3331   else if (element == EL_BD_AMOEBA)
3332   {
3333     level->amoeba_content = content_array[0][0][0];
3334   }
3335   else
3336   {
3337     Warn("cannot load content for element '%d'", element);
3338   }
3339
3340   return chunk_size;
3341 }
3342
3343 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3344 {
3345   int i;
3346   int element;
3347   int envelope_nr;
3348   int envelope_len;
3349   int chunk_size_expected;
3350
3351   element = getMappedElement(getFile16BitBE(file));
3352   if (!IS_ENVELOPE(element))
3353     element = EL_ENVELOPE_1;
3354
3355   envelope_nr = element - EL_ENVELOPE_1;
3356
3357   envelope_len = getFile16BitBE(file);
3358
3359   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3360   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3361
3362   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3363
3364   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3365   if (chunk_size_expected != chunk_size)
3366   {
3367     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3368     return chunk_size_expected;
3369   }
3370
3371   for (i = 0; i < envelope_len; i++)
3372     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3373
3374   return chunk_size;
3375 }
3376
3377 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3378 {
3379   int num_changed_custom_elements = getFile16BitBE(file);
3380   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3381   int i;
3382
3383   if (chunk_size_expected != chunk_size)
3384   {
3385     ReadUnusedBytesFromFile(file, chunk_size - 2);
3386     return chunk_size_expected;
3387   }
3388
3389   for (i = 0; i < num_changed_custom_elements; i++)
3390   {
3391     int element = getMappedElement(getFile16BitBE(file));
3392     int properties = getFile32BitBE(file);
3393
3394     if (IS_CUSTOM_ELEMENT(element))
3395       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3396     else
3397       Warn("invalid custom element number %d", element);
3398
3399     // older game versions that wrote level files with CUS1 chunks used
3400     // different default push delay values (not yet stored in level file)
3401     element_info[element].push_delay_fixed = 2;
3402     element_info[element].push_delay_random = 8;
3403   }
3404
3405   level->file_has_custom_elements = TRUE;
3406
3407   return chunk_size;
3408 }
3409
3410 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3411 {
3412   int num_changed_custom_elements = getFile16BitBE(file);
3413   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3414   int i;
3415
3416   if (chunk_size_expected != chunk_size)
3417   {
3418     ReadUnusedBytesFromFile(file, chunk_size - 2);
3419     return chunk_size_expected;
3420   }
3421
3422   for (i = 0; i < num_changed_custom_elements; i++)
3423   {
3424     int element = getMappedElement(getFile16BitBE(file));
3425     int custom_target_element = getMappedElement(getFile16BitBE(file));
3426
3427     if (IS_CUSTOM_ELEMENT(element))
3428       element_info[element].change->target_element = custom_target_element;
3429     else
3430       Warn("invalid custom element number %d", element);
3431   }
3432
3433   level->file_has_custom_elements = TRUE;
3434
3435   return chunk_size;
3436 }
3437
3438 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3439 {
3440   int num_changed_custom_elements = getFile16BitBE(file);
3441   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3442   int i, j, x, y;
3443
3444   if (chunk_size_expected != chunk_size)
3445   {
3446     ReadUnusedBytesFromFile(file, chunk_size - 2);
3447     return chunk_size_expected;
3448   }
3449
3450   for (i = 0; i < num_changed_custom_elements; i++)
3451   {
3452     int element = getMappedElement(getFile16BitBE(file));
3453     struct ElementInfo *ei = &element_info[element];
3454     unsigned int event_bits;
3455
3456     if (!IS_CUSTOM_ELEMENT(element))
3457     {
3458       Warn("invalid custom element number %d", element);
3459
3460       element = EL_INTERNAL_DUMMY;
3461     }
3462
3463     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3464       ei->description[j] = getFile8Bit(file);
3465     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3466
3467     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3468
3469     // some free bytes for future properties and padding
3470     ReadUnusedBytesFromFile(file, 7);
3471
3472     ei->use_gfx_element = getFile8Bit(file);
3473     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3474
3475     ei->collect_score_initial = getFile8Bit(file);
3476     ei->collect_count_initial = getFile8Bit(file);
3477
3478     ei->push_delay_fixed = getFile16BitBE(file);
3479     ei->push_delay_random = getFile16BitBE(file);
3480     ei->move_delay_fixed = getFile16BitBE(file);
3481     ei->move_delay_random = getFile16BitBE(file);
3482
3483     ei->move_pattern = getFile16BitBE(file);
3484     ei->move_direction_initial = getFile8Bit(file);
3485     ei->move_stepsize = getFile8Bit(file);
3486
3487     for (y = 0; y < 3; y++)
3488       for (x = 0; x < 3; x++)
3489         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3490
3491     // bits 0 - 31 of "has_event[]"
3492     event_bits = getFile32BitBE(file);
3493     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3494       if (event_bits & (1u << j))
3495         ei->change->has_event[j] = TRUE;
3496
3497     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3498
3499     ei->change->delay_fixed = getFile16BitBE(file);
3500     ei->change->delay_random = getFile16BitBE(file);
3501     ei->change->delay_frames = getFile16BitBE(file);
3502
3503     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3504
3505     ei->change->explode = getFile8Bit(file);
3506     ei->change->use_target_content = getFile8Bit(file);
3507     ei->change->only_if_complete = getFile8Bit(file);
3508     ei->change->use_random_replace = getFile8Bit(file);
3509
3510     ei->change->random_percentage = getFile8Bit(file);
3511     ei->change->replace_when = getFile8Bit(file);
3512
3513     for (y = 0; y < 3; y++)
3514       for (x = 0; x < 3; x++)
3515         ei->change->target_content.e[x][y] =
3516           getMappedElement(getFile16BitBE(file));
3517
3518     ei->slippery_type = getFile8Bit(file);
3519
3520     // some free bytes for future properties and padding
3521     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3522
3523     // mark that this custom element has been modified
3524     ei->modified_settings = TRUE;
3525   }
3526
3527   level->file_has_custom_elements = TRUE;
3528
3529   return chunk_size;
3530 }
3531
3532 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3533 {
3534   struct ElementInfo *ei;
3535   int chunk_size_expected;
3536   int element;
3537   int i, j, x, y;
3538
3539   // ---------- custom element base property values (96 bytes) ----------------
3540
3541   element = getMappedElement(getFile16BitBE(file));
3542
3543   if (!IS_CUSTOM_ELEMENT(element))
3544   {
3545     Warn("invalid custom element number %d", element);
3546
3547     ReadUnusedBytesFromFile(file, chunk_size - 2);
3548
3549     return chunk_size;
3550   }
3551
3552   ei = &element_info[element];
3553
3554   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3555     ei->description[i] = getFile8Bit(file);
3556   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3557
3558   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3559
3560   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3561
3562   ei->num_change_pages = getFile8Bit(file);
3563
3564   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3565   if (chunk_size_expected != chunk_size)
3566   {
3567     ReadUnusedBytesFromFile(file, chunk_size - 43);
3568     return chunk_size_expected;
3569   }
3570
3571   ei->ce_value_fixed_initial = getFile16BitBE(file);
3572   ei->ce_value_random_initial = getFile16BitBE(file);
3573   ei->use_last_ce_value = getFile8Bit(file);
3574
3575   ei->use_gfx_element = getFile8Bit(file);
3576   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3577
3578   ei->collect_score_initial = getFile8Bit(file);
3579   ei->collect_count_initial = getFile8Bit(file);
3580
3581   ei->drop_delay_fixed = getFile8Bit(file);
3582   ei->push_delay_fixed = getFile8Bit(file);
3583   ei->drop_delay_random = getFile8Bit(file);
3584   ei->push_delay_random = getFile8Bit(file);
3585   ei->move_delay_fixed = getFile16BitBE(file);
3586   ei->move_delay_random = getFile16BitBE(file);
3587
3588   // bits 0 - 15 of "move_pattern" ...
3589   ei->move_pattern = getFile16BitBE(file);
3590   ei->move_direction_initial = getFile8Bit(file);
3591   ei->move_stepsize = getFile8Bit(file);
3592
3593   ei->slippery_type = getFile8Bit(file);
3594
3595   for (y = 0; y < 3; y++)
3596     for (x = 0; x < 3; x++)
3597       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3598
3599   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3600   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3601   ei->move_leave_type = getFile8Bit(file);
3602
3603   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3604   ei->move_pattern |= (getFile16BitBE(file) << 16);
3605
3606   ei->access_direction = getFile8Bit(file);
3607
3608   ei->explosion_delay = getFile8Bit(file);
3609   ei->ignition_delay = getFile8Bit(file);
3610   ei->explosion_type = getFile8Bit(file);
3611
3612   // some free bytes for future custom property values and padding
3613   ReadUnusedBytesFromFile(file, 1);
3614
3615   // ---------- change page property values (48 bytes) ------------------------
3616
3617   setElementChangePages(ei, ei->num_change_pages);
3618
3619   for (i = 0; i < ei->num_change_pages; i++)
3620   {
3621     struct ElementChangeInfo *change = &ei->change_page[i];
3622     unsigned int event_bits;
3623
3624     // always start with reliable default values
3625     setElementChangeInfoToDefaults(change);
3626
3627     // bits 0 - 31 of "has_event[]" ...
3628     event_bits = getFile32BitBE(file);
3629     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3630       if (event_bits & (1u << j))
3631         change->has_event[j] = TRUE;
3632
3633     change->target_element = getMappedElement(getFile16BitBE(file));
3634
3635     change->delay_fixed = getFile16BitBE(file);
3636     change->delay_random = getFile16BitBE(file);
3637     change->delay_frames = getFile16BitBE(file);
3638
3639     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3640
3641     change->explode = getFile8Bit(file);
3642     change->use_target_content = getFile8Bit(file);
3643     change->only_if_complete = getFile8Bit(file);
3644     change->use_random_replace = getFile8Bit(file);
3645
3646     change->random_percentage = getFile8Bit(file);
3647     change->replace_when = getFile8Bit(file);
3648
3649     for (y = 0; y < 3; y++)
3650       for (x = 0; x < 3; x++)
3651         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3652
3653     change->can_change = getFile8Bit(file);
3654
3655     change->trigger_side = getFile8Bit(file);
3656
3657     change->trigger_player = getFile8Bit(file);
3658     change->trigger_page = getFile8Bit(file);
3659
3660     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3661                             CH_PAGE_ANY : (1 << change->trigger_page));
3662
3663     change->has_action = getFile8Bit(file);
3664     change->action_type = getFile8Bit(file);
3665     change->action_mode = getFile8Bit(file);
3666     change->action_arg = getFile16BitBE(file);
3667
3668     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3669     event_bits = getFile8Bit(file);
3670     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3671       if (event_bits & (1u << (j - 32)))
3672         change->has_event[j] = TRUE;
3673   }
3674
3675   // mark this custom element as modified
3676   ei->modified_settings = TRUE;
3677
3678   level->file_has_custom_elements = TRUE;
3679
3680   return chunk_size;
3681 }
3682
3683 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3684 {
3685   struct ElementInfo *ei;
3686   struct ElementGroupInfo *group;
3687   int element;
3688   int i;
3689
3690   element = getMappedElement(getFile16BitBE(file));
3691
3692   if (!IS_GROUP_ELEMENT(element))
3693   {
3694     Warn("invalid group element number %d", element);
3695
3696     ReadUnusedBytesFromFile(file, chunk_size - 2);
3697
3698     return chunk_size;
3699   }
3700
3701   ei = &element_info[element];
3702
3703   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3704     ei->description[i] = getFile8Bit(file);
3705   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3706
3707   group = element_info[element].group;
3708
3709   group->num_elements = getFile8Bit(file);
3710
3711   ei->use_gfx_element = getFile8Bit(file);
3712   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3713
3714   group->choice_mode = getFile8Bit(file);
3715
3716   // some free bytes for future values and padding
3717   ReadUnusedBytesFromFile(file, 3);
3718
3719   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3720     group->element[i] = getMappedElement(getFile16BitBE(file));
3721
3722   // mark this group element as modified
3723   element_info[element].modified_settings = TRUE;
3724
3725   level->file_has_custom_elements = TRUE;
3726
3727   return chunk_size;
3728 }
3729
3730 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3731                                 int element, int real_element)
3732 {
3733   int micro_chunk_size = 0;
3734   int conf_type = getFile8Bit(file);
3735   int byte_mask = conf_type & CONF_MASK_BYTES;
3736   boolean element_found = FALSE;
3737   int i;
3738
3739   micro_chunk_size += 1;
3740
3741   if (byte_mask == CONF_MASK_MULTI_BYTES)
3742   {
3743     int num_bytes = getFile16BitBE(file);
3744     byte *buffer = checked_malloc(num_bytes);
3745
3746     ReadBytesFromFile(file, buffer, num_bytes);
3747
3748     for (i = 0; conf[i].data_type != -1; i++)
3749     {
3750       if (conf[i].element == element &&
3751           conf[i].conf_type == conf_type)
3752       {
3753         int data_type = conf[i].data_type;
3754         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3755         int max_num_entities = conf[i].max_num_entities;
3756
3757         if (num_entities > max_num_entities)
3758         {
3759           Warn("truncating number of entities for element %d from %d to %d",
3760                element, num_entities, max_num_entities);
3761
3762           num_entities = max_num_entities;
3763         }
3764
3765         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3766                                   data_type == TYPE_CONTENT_LIST))
3767         {
3768           // for element and content lists, zero entities are not allowed
3769           Warn("found empty list of entities for element %d", element);
3770
3771           // do not set "num_entities" here to prevent reading behind buffer
3772
3773           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3774         }
3775         else
3776         {
3777           *(int *)(conf[i].num_entities) = num_entities;
3778         }
3779
3780         element_found = TRUE;
3781
3782         if (data_type == TYPE_STRING)
3783         {
3784           char *string = (char *)(conf[i].value);
3785           int j;
3786
3787           for (j = 0; j < max_num_entities; j++)
3788             string[j] = (j < num_entities ? buffer[j] : '\0');
3789         }
3790         else if (data_type == TYPE_ELEMENT_LIST)
3791         {
3792           int *element_array = (int *)(conf[i].value);
3793           int j;
3794
3795           for (j = 0; j < num_entities; j++)
3796             element_array[j] =
3797               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3798         }
3799         else if (data_type == TYPE_CONTENT_LIST)
3800         {
3801           struct Content *content= (struct Content *)(conf[i].value);
3802           int c, x, y;
3803
3804           for (c = 0; c < num_entities; c++)
3805             for (y = 0; y < 3; y++)
3806               for (x = 0; x < 3; x++)
3807                 content[c].e[x][y] =
3808                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3809         }
3810         else
3811           element_found = FALSE;
3812
3813         break;
3814       }
3815     }
3816
3817     checked_free(buffer);
3818
3819     micro_chunk_size += 2 + num_bytes;
3820   }
3821   else          // constant size configuration data (1, 2 or 4 bytes)
3822   {
3823     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3824                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3825                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3826
3827     for (i = 0; conf[i].data_type != -1; i++)
3828     {
3829       if (conf[i].element == element &&
3830           conf[i].conf_type == conf_type)
3831       {
3832         int data_type = conf[i].data_type;
3833
3834         if (data_type == TYPE_ELEMENT)
3835           value = getMappedElement(value);
3836
3837         if (data_type == TYPE_BOOLEAN)
3838           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3839         else
3840           *(int *)    (conf[i].value) = value;
3841
3842         element_found = TRUE;
3843
3844         break;
3845       }
3846     }
3847
3848     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3849   }
3850
3851   if (!element_found)
3852   {
3853     char *error_conf_chunk_bytes =
3854       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3855        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3856        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3857     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3858     int error_element = real_element;
3859
3860     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3861          error_conf_chunk_bytes, error_conf_chunk_token,
3862          error_element, EL_NAME(error_element));
3863   }
3864
3865   return micro_chunk_size;
3866 }
3867
3868 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3869 {
3870   int real_chunk_size = 0;
3871
3872   li = *level;          // copy level data into temporary buffer
3873
3874   while (!checkEndOfFile(file))
3875   {
3876     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3877
3878     if (real_chunk_size >= chunk_size)
3879       break;
3880   }
3881
3882   *level = li;          // copy temporary buffer back to level data
3883
3884   return real_chunk_size;
3885 }
3886
3887 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3888 {
3889   int real_chunk_size = 0;
3890
3891   li = *level;          // copy level data into temporary buffer
3892
3893   while (!checkEndOfFile(file))
3894   {
3895     int element = getMappedElement(getFile16BitBE(file));
3896
3897     real_chunk_size += 2;
3898     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3899                                             element, element);
3900     if (real_chunk_size >= chunk_size)
3901       break;
3902   }
3903
3904   *level = li;          // copy temporary buffer back to level data
3905
3906   return real_chunk_size;
3907 }
3908
3909 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3910 {
3911   int real_chunk_size = 0;
3912
3913   li = *level;          // copy level data into temporary buffer
3914
3915   while (!checkEndOfFile(file))
3916   {
3917     int element = getMappedElement(getFile16BitBE(file));
3918
3919     real_chunk_size += 2;
3920     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3921                                             element, element);
3922     if (real_chunk_size >= chunk_size)
3923       break;
3924   }
3925
3926   *level = li;          // copy temporary buffer back to level data
3927
3928   return real_chunk_size;
3929 }
3930
3931 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3932 {
3933   int element = getMappedElement(getFile16BitBE(file));
3934   int envelope_nr = element - EL_ENVELOPE_1;
3935   int real_chunk_size = 2;
3936
3937   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3938
3939   while (!checkEndOfFile(file))
3940   {
3941     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3942                                             -1, element);
3943
3944     if (real_chunk_size >= chunk_size)
3945       break;
3946   }
3947
3948   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3949
3950   return real_chunk_size;
3951 }
3952
3953 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3954 {
3955   int element = getMappedElement(getFile16BitBE(file));
3956   int real_chunk_size = 2;
3957   struct ElementInfo *ei = &element_info[element];
3958   int i;
3959
3960   xx_ei = *ei;          // copy element data into temporary buffer
3961
3962   xx_ei.num_change_pages = -1;
3963
3964   while (!checkEndOfFile(file))
3965   {
3966     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3967                                             -1, element);
3968     if (xx_ei.num_change_pages != -1)
3969       break;
3970
3971     if (real_chunk_size >= chunk_size)
3972       break;
3973   }
3974
3975   *ei = xx_ei;
3976
3977   if (ei->num_change_pages == -1)
3978   {
3979     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3980          EL_NAME(element));
3981
3982     ei->num_change_pages = 1;
3983
3984     setElementChangePages(ei, 1);
3985     setElementChangeInfoToDefaults(ei->change);
3986
3987     return real_chunk_size;
3988   }
3989
3990   // initialize number of change pages stored for this custom element
3991   setElementChangePages(ei, ei->num_change_pages);
3992   for (i = 0; i < ei->num_change_pages; i++)
3993     setElementChangeInfoToDefaults(&ei->change_page[i]);
3994
3995   // start with reading properties for the first change page
3996   xx_current_change_page = 0;
3997
3998   while (!checkEndOfFile(file))
3999   {
4000     // level file might contain invalid change page number
4001     if (xx_current_change_page >= ei->num_change_pages)
4002       break;
4003
4004     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4005
4006     xx_change = *change;        // copy change data into temporary buffer
4007
4008     resetEventBits();           // reset bits; change page might have changed
4009
4010     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4011                                             -1, element);
4012
4013     *change = xx_change;
4014
4015     setEventFlagsFromEventBits(change);
4016
4017     if (real_chunk_size >= chunk_size)
4018       break;
4019   }
4020
4021   level->file_has_custom_elements = TRUE;
4022
4023   return real_chunk_size;
4024 }
4025
4026 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4027 {
4028   int element = getMappedElement(getFile16BitBE(file));
4029   int real_chunk_size = 2;
4030   struct ElementInfo *ei = &element_info[element];
4031   struct ElementGroupInfo *group = ei->group;
4032
4033   if (group == NULL)
4034     return -1;
4035
4036   xx_ei = *ei;          // copy element data into temporary buffer
4037   xx_group = *group;    // copy group data into temporary buffer
4038
4039   while (!checkEndOfFile(file))
4040   {
4041     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4042                                             -1, element);
4043
4044     if (real_chunk_size >= chunk_size)
4045       break;
4046   }
4047
4048   *ei = xx_ei;
4049   *group = xx_group;
4050
4051   level->file_has_custom_elements = TRUE;
4052
4053   return real_chunk_size;
4054 }
4055
4056 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4057 {
4058   int element = getMappedElement(getFile16BitBE(file));
4059   int real_chunk_size = 2;
4060   struct ElementInfo *ei = &element_info[element];
4061
4062   xx_ei = *ei;          // copy element data into temporary buffer
4063
4064   while (!checkEndOfFile(file))
4065   {
4066     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4067                                             -1, element);
4068
4069     if (real_chunk_size >= chunk_size)
4070       break;
4071   }
4072
4073   *ei = xx_ei;
4074
4075   level->file_has_custom_elements = TRUE;
4076
4077   return real_chunk_size;
4078 }
4079
4080 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4081                                       struct LevelFileInfo *level_file_info,
4082                                       boolean level_info_only)
4083 {
4084   char *filename = level_file_info->filename;
4085   char cookie[MAX_LINE_LEN];
4086   char chunk_name[CHUNK_ID_LEN + 1];
4087   int chunk_size;
4088   File *file;
4089
4090   if (!(file = openFile(filename, MODE_READ)))
4091   {
4092     level->no_valid_file = TRUE;
4093     level->no_level_file = TRUE;
4094
4095     if (level_info_only)
4096       return;
4097
4098     Warn("cannot read level '%s' -- using empty level", filename);
4099
4100     if (!setup.editor.use_template_for_new_levels)
4101       return;
4102
4103     // if level file not found, try to initialize level data from template
4104     filename = getGlobalLevelTemplateFilename();
4105
4106     if (!(file = openFile(filename, MODE_READ)))
4107       return;
4108
4109     // default: for empty levels, use level template for custom elements
4110     level->use_custom_template = TRUE;
4111
4112     level->no_valid_file = FALSE;
4113   }
4114
4115   getFileChunkBE(file, chunk_name, NULL);
4116   if (strEqual(chunk_name, "RND1"))
4117   {
4118     getFile32BitBE(file);               // not used
4119
4120     getFileChunkBE(file, chunk_name, NULL);
4121     if (!strEqual(chunk_name, "CAVE"))
4122     {
4123       level->no_valid_file = TRUE;
4124
4125       Warn("unknown format of level file '%s'", filename);
4126
4127       closeFile(file);
4128
4129       return;
4130     }
4131   }
4132   else  // check for pre-2.0 file format with cookie string
4133   {
4134     strcpy(cookie, chunk_name);
4135     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4136       cookie[4] = '\0';
4137     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4138       cookie[strlen(cookie) - 1] = '\0';
4139
4140     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4141     {
4142       level->no_valid_file = TRUE;
4143
4144       Warn("unknown format of level file '%s'", filename);
4145
4146       closeFile(file);
4147
4148       return;
4149     }
4150
4151     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4152     {
4153       level->no_valid_file = TRUE;
4154
4155       Warn("unsupported version of level file '%s'", filename);
4156
4157       closeFile(file);
4158
4159       return;
4160     }
4161
4162     // pre-2.0 level files have no game version, so use file version here
4163     level->game_version = level->file_version;
4164   }
4165
4166   if (level->file_version < FILE_VERSION_1_2)
4167   {
4168     // level files from versions before 1.2.0 without chunk structure
4169     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4170     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4171   }
4172   else
4173   {
4174     static struct
4175     {
4176       char *name;
4177       int size;
4178       int (*loader)(File *, int, struct LevelInfo *);
4179     }
4180     chunk_info[] =
4181     {
4182       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4183       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4184       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4185       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4186       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4187       { "INFO", -1,                     LoadLevel_INFO },
4188       { "BODY", -1,                     LoadLevel_BODY },
4189       { "CONT", -1,                     LoadLevel_CONT },
4190       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4191       { "CNT3", -1,                     LoadLevel_CNT3 },
4192       { "CUS1", -1,                     LoadLevel_CUS1 },
4193       { "CUS2", -1,                     LoadLevel_CUS2 },
4194       { "CUS3", -1,                     LoadLevel_CUS3 },
4195       { "CUS4", -1,                     LoadLevel_CUS4 },
4196       { "GRP1", -1,                     LoadLevel_GRP1 },
4197       { "CONF", -1,                     LoadLevel_CONF },
4198       { "ELEM", -1,                     LoadLevel_ELEM },
4199       { "NOTE", -1,                     LoadLevel_NOTE },
4200       { "CUSX", -1,                     LoadLevel_CUSX },
4201       { "GRPX", -1,                     LoadLevel_GRPX },
4202       { "EMPX", -1,                     LoadLevel_EMPX },
4203
4204       {  NULL,  0,                      NULL }
4205     };
4206
4207     while (getFileChunkBE(file, chunk_name, &chunk_size))
4208     {
4209       int i = 0;
4210
4211       while (chunk_info[i].name != NULL &&
4212              !strEqual(chunk_name, chunk_info[i].name))
4213         i++;
4214
4215       if (chunk_info[i].name == NULL)
4216       {
4217         Warn("unknown chunk '%s' in level file '%s'",
4218              chunk_name, filename);
4219
4220         ReadUnusedBytesFromFile(file, chunk_size);
4221       }
4222       else if (chunk_info[i].size != -1 &&
4223                chunk_info[i].size != chunk_size)
4224       {
4225         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4226              chunk_size, chunk_name, filename);
4227
4228         ReadUnusedBytesFromFile(file, chunk_size);
4229       }
4230       else
4231       {
4232         // call function to load this level chunk
4233         int chunk_size_expected =
4234           (chunk_info[i].loader)(file, chunk_size, level);
4235
4236         if (chunk_size_expected < 0)
4237         {
4238           Warn("error reading chunk '%s' in level file '%s'",
4239                chunk_name, filename);
4240
4241           break;
4242         }
4243
4244         // the size of some chunks cannot be checked before reading other
4245         // chunks first (like "HEAD" and "BODY") that contain some header
4246         // information, so check them here
4247         if (chunk_size_expected != chunk_size)
4248         {
4249           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4250                chunk_size, chunk_name, filename);
4251
4252           break;
4253         }
4254       }
4255     }
4256   }
4257
4258   closeFile(file);
4259 }
4260
4261
4262 // ----------------------------------------------------------------------------
4263 // functions for loading BD level
4264 // ----------------------------------------------------------------------------
4265
4266 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4267 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4268
4269 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4270 {
4271   struct LevelInfo_BD *level_bd = level->native_bd_level;
4272   GdCave *cave = NULL;  // will be changed below
4273   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4274   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4275   int x, y;
4276
4277   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4278
4279   // cave and map newly allocated when set to defaults above
4280   cave = level_bd->cave;
4281
4282   // level type
4283   cave->intermission                    = level->bd_intermission;
4284
4285   // level settings
4286   cave->level_time[0]                   = level->time;
4287   cave->level_diamonds[0]               = level->gems_needed;
4288
4289   // game timing
4290   cave->scheduling                      = level->bd_scheduling_type;
4291   cave->pal_timing                      = level->bd_pal_timing;
4292   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4293   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4294   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4295   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4296
4297   // scores
4298   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4299   cave->diamond_value                   = level->score[SC_EMERALD];
4300   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4301
4302   // compatibility settings
4303   cave->lineshift                       = level->bd_line_shifting_borders;
4304   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4305   cave->short_explosions                = level->bd_short_explosions;
4306
4307   // player properties
4308   cave->diagonal_movements              = level->bd_diagonal_movements;
4309   cave->active_is_first_found           = level->bd_topmost_player_active;
4310   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4311   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4312   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4313   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4314
4315   // element properties
4316   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4317   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4318   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4319   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4320   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4321   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4322   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4323   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4324   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4325   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4326   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4327
4328   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4329   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4330   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4331   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4332   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4333   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4334   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4335
4336   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4337   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4338   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4339   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4340   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4341   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4342   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4343   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4344   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4345   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4346   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4347
4348   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4349   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4350   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4351   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4352   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4353   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4354
4355   cave->slime_predictable               = level->bd_slime_is_predictable;
4356   cave->slime_correct_random            = level->bd_slime_correct_random;
4357   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4358   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4359   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4360   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4361   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4362   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4363   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4364   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4365   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4366   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4367
4368   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4369   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4370   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4371
4372   cave->biter_delay_frame               = level->bd_biter_move_delay;
4373   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4374
4375   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4376
4377   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4378
4379   cave->replicators_active              = level->bd_replicators_active;
4380   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4381
4382   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4383   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4384
4385   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4386
4387   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4388
4389   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4390   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4391   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4392
4393   cave->infinite_rockets                = level->bd_infinite_rockets;
4394
4395   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4396   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4397
4398   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4399   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4400
4401   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4402   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4403   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4404
4405   cave->gravity                         = level->bd_gravity_direction;
4406   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4407   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4408   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4409
4410   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4411   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4412   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4413   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4414
4415   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4416   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4417   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4418   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4419   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4420   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4421
4422   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4423   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4424   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4425   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4426
4427   cave->colorb                          = level->bd_color_b;
4428   cave->color0                          = level->bd_color_0;
4429   cave->color1                          = level->bd_color_1;
4430   cave->color2                          = level->bd_color_2;
4431   cave->color3                          = level->bd_color_3;
4432   cave->color4                          = level->bd_color_4;
4433   cave->color5                          = level->bd_color_5;
4434
4435   // level name
4436   strncpy(cave->name, level->name, sizeof(GdString));
4437   cave->name[sizeof(GdString) - 1] = '\0';
4438
4439   // playfield elements
4440   for (x = 0; x < cave->w; x++)
4441     for (y = 0; y < cave->h; y++)
4442       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4443 }
4444
4445 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4446 {
4447   struct LevelInfo_BD *level_bd = level->native_bd_level;
4448   GdCave *cave = level_bd->cave;
4449   int bd_level_nr = level_bd->level_nr;
4450   int x, y;
4451
4452   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4453   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4454
4455   // level type
4456   level->bd_intermission                = cave->intermission;
4457
4458   // level settings
4459   level->time                           = cave->level_time[bd_level_nr];
4460   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4461
4462   // game timing
4463   level->bd_scheduling_type             = cave->scheduling;
4464   level->bd_pal_timing                  = cave->pal_timing;
4465   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4466   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4467   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4468   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4469
4470   // scores
4471   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4472   level->score[SC_EMERALD]              = cave->diamond_value;
4473   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4474
4475   // compatibility settings
4476   level->bd_line_shifting_borders       = cave->lineshift;
4477   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4478   level->bd_short_explosions            = cave->short_explosions;
4479
4480   // player properties
4481   level->bd_diagonal_movements          = cave->diagonal_movements;
4482   level->bd_topmost_player_active       = cave->active_is_first_found;
4483   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4484   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4485   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4486   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4487
4488   // element properties
4489   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4490   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4491   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4492   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4493   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4494   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4495   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4496   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4497   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4498   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4499   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4500
4501   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4502   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4503   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4504   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4505   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4506   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4507   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4508
4509   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4510   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4511   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4512   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4513   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4514   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4515   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4516   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4517   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4518   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4519   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4520
4521   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4522   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4523   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4524   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4525   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4526   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4527
4528   level->bd_slime_is_predictable        = cave->slime_predictable;
4529   level->bd_slime_correct_random        = cave->slime_correct_random;
4530   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4531   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4532   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4533   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4534   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4535   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4536   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4537   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4538   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4539   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4540
4541   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4542   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4543   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4544
4545   level->bd_biter_move_delay            = cave->biter_delay_frame;
4546   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4547
4548   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4549
4550   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4551
4552   level->bd_replicators_active          = cave->replicators_active;
4553   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4554
4555   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4556   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4557
4558   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4559
4560   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4561
4562   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4563   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4564   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4565
4566   level->bd_infinite_rockets            = cave->infinite_rockets;
4567
4568   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4569   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4570
4571   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4572   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4573
4574   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4575   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4576   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4577
4578   level->bd_gravity_direction           = cave->gravity;
4579   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4580   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4581   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4582
4583   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4584   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4585   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4586   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4587
4588   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4589   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4590   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4591   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4592   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4593   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4594
4595   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4596   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4597   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4598   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4599
4600   level->bd_color_b                     = cave->colorb;
4601   level->bd_color_0                     = cave->color0;
4602   level->bd_color_1                     = cave->color1;
4603   level->bd_color_2                     = cave->color2;
4604   level->bd_color_3                     = cave->color3;
4605   level->bd_color_4                     = cave->color4;
4606   level->bd_color_5                     = cave->color5;
4607
4608   // set default color type and colors for BD style level colors
4609   SetDefaultLevelColorType_BD();
4610   SetDefaultLevelColors_BD();
4611
4612   // level name
4613   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4614
4615   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4616   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4617
4618   // playfield elements
4619   for (x = 0; x < level->fieldx; x++)
4620     for (y = 0; y < level->fieldy; y++)
4621       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4622
4623   checked_free(cave_name);
4624 }
4625
4626 static void setTapeInfoToDefaults(void);
4627
4628 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4629 {
4630   struct LevelInfo_BD *level_bd = level->native_bd_level;
4631   GdCave *cave = level_bd->cave;
4632   GdReplay *replay = level_bd->replay;
4633   int i;
4634
4635   if (replay == NULL)
4636     return;
4637
4638   // always start with reliable default values
4639   setTapeInfoToDefaults();
4640
4641   tape.level_nr = level_nr;             // (currently not used)
4642   tape.random_seed = replay->seed;
4643
4644   TapeSetDateFromIsoDateString(replay->date);
4645
4646   tape.counter = 0;
4647   tape.pos[tape.counter].delay = 0;
4648
4649   tape.bd_replay = TRUE;
4650
4651   // all time calculations only used to display approximate tape time
4652   int cave_speed = cave->speed;
4653   int milliseconds_game = 0;
4654   int milliseconds_elapsed = 20;
4655
4656   for (i = 0; i < replay->movements->len; i++)
4657   {
4658     int replay_action = replay->movements->data[i];
4659     int tape_action = map_action_BD_to_RND(replay_action);
4660     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4661     boolean success = 0;
4662
4663     while (1)
4664     {
4665       success = TapeAddAction(action);
4666
4667       milliseconds_game += milliseconds_elapsed;
4668
4669       if (milliseconds_game >= cave_speed)
4670       {
4671         milliseconds_game -= cave_speed;
4672
4673         break;
4674       }
4675     }
4676
4677     tape.counter++;
4678     tape.pos[tape.counter].delay = 0;
4679     tape.pos[tape.counter].action[0] = 0;
4680
4681     if (!success)
4682     {
4683       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4684
4685       break;
4686     }
4687   }
4688
4689   TapeHaltRecording();
4690
4691   if (!replay->success)
4692     Warn("BD replay is marked as not successful");
4693 }
4694
4695
4696 // ----------------------------------------------------------------------------
4697 // functions for loading EM level
4698 // ----------------------------------------------------------------------------
4699
4700 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4701 {
4702   static int ball_xy[8][2] =
4703   {
4704     { 0, 0 },
4705     { 1, 0 },
4706     { 2, 0 },
4707     { 0, 1 },
4708     { 2, 1 },
4709     { 0, 2 },
4710     { 1, 2 },
4711     { 2, 2 },
4712   };
4713   struct LevelInfo_EM *level_em = level->native_em_level;
4714   struct CAVE *cav = level_em->cav;
4715   int i, j, x, y;
4716
4717   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4718   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4719
4720   cav->time_seconds     = level->time;
4721   cav->gems_needed      = level->gems_needed;
4722
4723   cav->emerald_score    = level->score[SC_EMERALD];
4724   cav->diamond_score    = level->score[SC_DIAMOND];
4725   cav->alien_score      = level->score[SC_ROBOT];
4726   cav->tank_score       = level->score[SC_SPACESHIP];
4727   cav->bug_score        = level->score[SC_BUG];
4728   cav->eater_score      = level->score[SC_YAMYAM];
4729   cav->nut_score        = level->score[SC_NUT];
4730   cav->dynamite_score   = level->score[SC_DYNAMITE];
4731   cav->key_score        = level->score[SC_KEY];
4732   cav->exit_score       = level->score[SC_TIME_BONUS];
4733
4734   cav->num_eater_arrays = level->num_yamyam_contents;
4735
4736   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4737     for (y = 0; y < 3; y++)
4738       for (x = 0; x < 3; x++)
4739         cav->eater_array[i][y * 3 + x] =
4740           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4741
4742   cav->amoeba_time              = level->amoeba_speed;
4743   cav->wonderwall_time          = level->time_magic_wall;
4744   cav->wheel_time               = level->time_wheel;
4745
4746   cav->android_move_time        = level->android_move_time;
4747   cav->android_clone_time       = level->android_clone_time;
4748   cav->ball_random              = level->ball_random;
4749   cav->ball_active              = level->ball_active_initial;
4750   cav->ball_time                = level->ball_time;
4751   cav->num_ball_arrays          = level->num_ball_contents;
4752
4753   cav->lenses_score             = level->lenses_score;
4754   cav->magnify_score            = level->magnify_score;
4755   cav->slurp_score              = level->slurp_score;
4756
4757   cav->lenses_time              = level->lenses_time;
4758   cav->magnify_time             = level->magnify_time;
4759
4760   cav->wind_time = 9999;
4761   cav->wind_direction =
4762     map_direction_RND_to_EM(level->wind_direction_initial);
4763
4764   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4765     for (j = 0; j < 8; j++)
4766       cav->ball_array[i][j] =
4767         map_element_RND_to_EM_cave(level->ball_content[i].
4768                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4769
4770   map_android_clone_elements_RND_to_EM(level);
4771
4772   // first fill the complete playfield with the empty space element
4773   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4774     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4775       cav->cave[x][y] = Cblank;
4776
4777   // then copy the real level contents from level file into the playfield
4778   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4779   {
4780     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4781
4782     if (level->field[x][y] == EL_AMOEBA_DEAD)
4783       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4784
4785     cav->cave[x][y] = new_element;
4786   }
4787
4788   for (i = 0; i < MAX_PLAYERS; i++)
4789   {
4790     cav->player_x[i] = -1;
4791     cav->player_y[i] = -1;
4792   }
4793
4794   // initialize player positions and delete players from the playfield
4795   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4796   {
4797     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4798     {
4799       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4800
4801       cav->player_x[player_nr] = x;
4802       cav->player_y[player_nr] = y;
4803
4804       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4805     }
4806   }
4807 }
4808
4809 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4810 {
4811   static int ball_xy[8][2] =
4812   {
4813     { 0, 0 },
4814     { 1, 0 },
4815     { 2, 0 },
4816     { 0, 1 },
4817     { 2, 1 },
4818     { 0, 2 },
4819     { 1, 2 },
4820     { 2, 2 },
4821   };
4822   struct LevelInfo_EM *level_em = level->native_em_level;
4823   struct CAVE *cav = level_em->cav;
4824   int i, j, x, y;
4825
4826   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4827   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4828
4829   level->time        = cav->time_seconds;
4830   level->gems_needed = cav->gems_needed;
4831
4832   sprintf(level->name, "Level %d", level->file_info.nr);
4833
4834   level->score[SC_EMERALD]      = cav->emerald_score;
4835   level->score[SC_DIAMOND]      = cav->diamond_score;
4836   level->score[SC_ROBOT]        = cav->alien_score;
4837   level->score[SC_SPACESHIP]    = cav->tank_score;
4838   level->score[SC_BUG]          = cav->bug_score;
4839   level->score[SC_YAMYAM]       = cav->eater_score;
4840   level->score[SC_NUT]          = cav->nut_score;
4841   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4842   level->score[SC_KEY]          = cav->key_score;
4843   level->score[SC_TIME_BONUS]   = cav->exit_score;
4844
4845   level->num_yamyam_contents    = cav->num_eater_arrays;
4846
4847   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4848     for (y = 0; y < 3; y++)
4849       for (x = 0; x < 3; x++)
4850         level->yamyam_content[i].e[x][y] =
4851           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4852
4853   level->amoeba_speed           = cav->amoeba_time;
4854   level->time_magic_wall        = cav->wonderwall_time;
4855   level->time_wheel             = cav->wheel_time;
4856
4857   level->android_move_time      = cav->android_move_time;
4858   level->android_clone_time     = cav->android_clone_time;
4859   level->ball_random            = cav->ball_random;
4860   level->ball_active_initial    = cav->ball_active;
4861   level->ball_time              = cav->ball_time;
4862   level->num_ball_contents      = cav->num_ball_arrays;
4863
4864   level->lenses_score           = cav->lenses_score;
4865   level->magnify_score          = cav->magnify_score;
4866   level->slurp_score            = cav->slurp_score;
4867
4868   level->lenses_time            = cav->lenses_time;
4869   level->magnify_time           = cav->magnify_time;
4870
4871   level->wind_direction_initial =
4872     map_direction_EM_to_RND(cav->wind_direction);
4873
4874   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4875     for (j = 0; j < 8; j++)
4876       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4877         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4878
4879   map_android_clone_elements_EM_to_RND(level);
4880
4881   // convert the playfield (some elements need special treatment)
4882   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4883   {
4884     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4885
4886     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4887       new_element = EL_AMOEBA_DEAD;
4888
4889     level->field[x][y] = new_element;
4890   }
4891
4892   for (i = 0; i < MAX_PLAYERS; i++)
4893   {
4894     // in case of all players set to the same field, use the first player
4895     int nr = MAX_PLAYERS - i - 1;
4896     int jx = cav->player_x[nr];
4897     int jy = cav->player_y[nr];
4898
4899     if (jx != -1 && jy != -1)
4900       level->field[jx][jy] = EL_PLAYER_1 + nr;
4901   }
4902
4903   // time score is counted for each 10 seconds left in Emerald Mine levels
4904   level->time_score_base = 10;
4905 }
4906
4907
4908 // ----------------------------------------------------------------------------
4909 // functions for loading SP level
4910 // ----------------------------------------------------------------------------
4911
4912 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4913 {
4914   struct LevelInfo_SP *level_sp = level->native_sp_level;
4915   LevelInfoType *header = &level_sp->header;
4916   int i, x, y;
4917
4918   level_sp->width  = level->fieldx;
4919   level_sp->height = level->fieldy;
4920
4921   for (x = 0; x < level->fieldx; x++)
4922     for (y = 0; y < level->fieldy; y++)
4923       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4924
4925   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4926
4927   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4928     header->LevelTitle[i] = level->name[i];
4929   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4930
4931   header->InfotronsNeeded = level->gems_needed;
4932
4933   header->SpecialPortCount = 0;
4934
4935   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4936   {
4937     boolean gravity_port_found = FALSE;
4938     boolean gravity_port_valid = FALSE;
4939     int gravity_port_flag;
4940     int gravity_port_base_element;
4941     int element = level->field[x][y];
4942
4943     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4944         element <= EL_SP_GRAVITY_ON_PORT_UP)
4945     {
4946       gravity_port_found = TRUE;
4947       gravity_port_valid = TRUE;
4948       gravity_port_flag = 1;
4949       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4950     }
4951     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4952              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4953     {
4954       gravity_port_found = TRUE;
4955       gravity_port_valid = TRUE;
4956       gravity_port_flag = 0;
4957       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4958     }
4959     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4960              element <= EL_SP_GRAVITY_PORT_UP)
4961     {
4962       // change R'n'D style gravity inverting special port to normal port
4963       // (there are no gravity inverting ports in native Supaplex engine)
4964
4965       gravity_port_found = TRUE;
4966       gravity_port_valid = FALSE;
4967       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4968     }
4969
4970     if (gravity_port_found)
4971     {
4972       if (gravity_port_valid &&
4973           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4974       {
4975         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4976
4977         port->PortLocation = (y * level->fieldx + x) * 2;
4978         port->Gravity = gravity_port_flag;
4979
4980         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4981
4982         header->SpecialPortCount++;
4983       }
4984       else
4985       {
4986         // change special gravity port to normal port
4987
4988         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4989       }
4990
4991       level_sp->playfield[x][y] = element - EL_SP_START;
4992     }
4993   }
4994 }
4995
4996 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4997 {
4998   struct LevelInfo_SP *level_sp = level->native_sp_level;
4999   LevelInfoType *header = &level_sp->header;
5000   boolean num_invalid_elements = 0;
5001   int i, j, x, y;
5002
5003   level->fieldx = level_sp->width;
5004   level->fieldy = level_sp->height;
5005
5006   for (x = 0; x < level->fieldx; x++)
5007   {
5008     for (y = 0; y < level->fieldy; y++)
5009     {
5010       int element_old = level_sp->playfield[x][y];
5011       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5012
5013       if (element_new == EL_UNKNOWN)
5014       {
5015         num_invalid_elements++;
5016
5017         Debug("level:native:SP", "invalid element %d at position %d, %d",
5018               element_old, x, y);
5019       }
5020
5021       level->field[x][y] = element_new;
5022     }
5023   }
5024
5025   if (num_invalid_elements > 0)
5026     Warn("found %d invalid elements%s", num_invalid_elements,
5027          (!options.debug ? " (use '--debug' for more details)" : ""));
5028
5029   for (i = 0; i < MAX_PLAYERS; i++)
5030     level->initial_player_gravity[i] =
5031       (header->InitialGravity == 1 ? TRUE : FALSE);
5032
5033   // skip leading spaces
5034   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5035     if (header->LevelTitle[i] != ' ')
5036       break;
5037
5038   // copy level title
5039   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5040     level->name[j] = header->LevelTitle[i];
5041   level->name[j] = '\0';
5042
5043   // cut trailing spaces
5044   for (; j > 0; j--)
5045     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5046       level->name[j - 1] = '\0';
5047
5048   level->gems_needed = header->InfotronsNeeded;
5049
5050   for (i = 0; i < header->SpecialPortCount; i++)
5051   {
5052     SpecialPortType *port = &header->SpecialPort[i];
5053     int port_location = port->PortLocation;
5054     int gravity = port->Gravity;
5055     int port_x, port_y, port_element;
5056
5057     port_x = (port_location / 2) % level->fieldx;
5058     port_y = (port_location / 2) / level->fieldx;
5059
5060     if (port_x < 0 || port_x >= level->fieldx ||
5061         port_y < 0 || port_y >= level->fieldy)
5062     {
5063       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5064
5065       continue;
5066     }
5067
5068     port_element = level->field[port_x][port_y];
5069
5070     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5071         port_element > EL_SP_GRAVITY_PORT_UP)
5072     {
5073       Warn("no special port at position (%d, %d)", port_x, port_y);
5074
5075       continue;
5076     }
5077
5078     // change previous (wrong) gravity inverting special port to either
5079     // gravity enabling special port or gravity disabling special port
5080     level->field[port_x][port_y] +=
5081       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5082        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5083   }
5084
5085   // change special gravity ports without database entries to normal ports
5086   for (x = 0; x < level->fieldx; x++)
5087     for (y = 0; y < level->fieldy; y++)
5088       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5089           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5090         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5091
5092   level->time = 0;                      // no time limit
5093   level->amoeba_speed = 0;
5094   level->time_magic_wall = 0;
5095   level->time_wheel = 0;
5096   level->amoeba_content = EL_EMPTY;
5097
5098   // original Supaplex does not use score values -- rate by playing time
5099   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5100     level->score[i] = 0;
5101
5102   level->rate_time_over_score = TRUE;
5103
5104   // there are no yamyams in supaplex levels
5105   for (i = 0; i < level->num_yamyam_contents; i++)
5106     for (x = 0; x < 3; x++)
5107       for (y = 0; y < 3; y++)
5108         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5109 }
5110
5111 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5112 {
5113   struct LevelInfo_SP *level_sp = level->native_sp_level;
5114   struct DemoInfo_SP *demo = &level_sp->demo;
5115   int i, j;
5116
5117   // always start with reliable default values
5118   demo->is_available = FALSE;
5119   demo->length = 0;
5120
5121   if (TAPE_IS_EMPTY(tape))
5122     return;
5123
5124   demo->level_nr = tape.level_nr;       // (currently not used)
5125
5126   level_sp->header.DemoRandomSeed = tape.random_seed;
5127
5128   demo->length = 0;
5129
5130   for (i = 0; i < tape.length; i++)
5131   {
5132     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5133     int demo_repeat = tape.pos[i].delay;
5134     int demo_entries = (demo_repeat + 15) / 16;
5135
5136     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5137     {
5138       Warn("tape truncated: size exceeds maximum SP demo size %d",
5139            SP_MAX_TAPE_LEN);
5140
5141       break;
5142     }
5143
5144     for (j = 0; j < demo_repeat / 16; j++)
5145       demo->data[demo->length++] = 0xf0 | demo_action;
5146
5147     if (demo_repeat % 16)
5148       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5149   }
5150
5151   demo->is_available = TRUE;
5152 }
5153
5154 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5155 {
5156   struct LevelInfo_SP *level_sp = level->native_sp_level;
5157   struct DemoInfo_SP *demo = &level_sp->demo;
5158   char *filename = level->file_info.filename;
5159   int i;
5160
5161   // always start with reliable default values
5162   setTapeInfoToDefaults();
5163
5164   if (!demo->is_available)
5165     return;
5166
5167   tape.level_nr = demo->level_nr;       // (currently not used)
5168   tape.random_seed = level_sp->header.DemoRandomSeed;
5169
5170   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5171
5172   tape.counter = 0;
5173   tape.pos[tape.counter].delay = 0;
5174
5175   for (i = 0; i < demo->length; i++)
5176   {
5177     int demo_action = demo->data[i] & 0x0f;
5178     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5179     int tape_action = map_key_SP_to_RND(demo_action);
5180     int tape_repeat = demo_repeat + 1;
5181     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5182     boolean success = 0;
5183     int j;
5184
5185     for (j = 0; j < tape_repeat; j++)
5186       success = TapeAddAction(action);
5187
5188     if (!success)
5189     {
5190       Warn("SP demo truncated: size exceeds maximum tape size %d",
5191            MAX_TAPE_LEN);
5192
5193       break;
5194     }
5195   }
5196
5197   TapeHaltRecording();
5198 }
5199
5200
5201 // ----------------------------------------------------------------------------
5202 // functions for loading MM level
5203 // ----------------------------------------------------------------------------
5204
5205 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5206 {
5207   struct LevelInfo_MM *level_mm = level->native_mm_level;
5208   int i, x, y;
5209
5210   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5211   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5212
5213   level_mm->time = level->time;
5214   level_mm->kettles_needed = level->gems_needed;
5215   level_mm->auto_count_kettles = level->auto_count_gems;
5216
5217   level_mm->mm_laser_red   = level->mm_laser_red;
5218   level_mm->mm_laser_green = level->mm_laser_green;
5219   level_mm->mm_laser_blue  = level->mm_laser_blue;
5220
5221   level_mm->df_laser_red   = level->df_laser_red;
5222   level_mm->df_laser_green = level->df_laser_green;
5223   level_mm->df_laser_blue  = level->df_laser_blue;
5224
5225   strcpy(level_mm->name, level->name);
5226   strcpy(level_mm->author, level->author);
5227
5228   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5229   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5230   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5231   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5232   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5233
5234   level_mm->amoeba_speed = level->amoeba_speed;
5235   level_mm->time_fuse    = level->mm_time_fuse;
5236   level_mm->time_bomb    = level->mm_time_bomb;
5237   level_mm->time_ball    = level->mm_time_ball;
5238   level_mm->time_block   = level->mm_time_block;
5239
5240   level_mm->num_ball_contents = level->num_mm_ball_contents;
5241   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5242   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5243   level_mm->explode_ball = level->explode_mm_ball;
5244
5245   for (i = 0; i < level->num_mm_ball_contents; i++)
5246     level_mm->ball_content[i] =
5247       map_element_RND_to_MM(level->mm_ball_content[i]);
5248
5249   for (x = 0; x < level->fieldx; x++)
5250     for (y = 0; y < level->fieldy; y++)
5251       Ur[x][y] =
5252         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5253 }
5254
5255 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5256 {
5257   struct LevelInfo_MM *level_mm = level->native_mm_level;
5258   int i, x, y;
5259
5260   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5261   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5262
5263   level->time = level_mm->time;
5264   level->gems_needed = level_mm->kettles_needed;
5265   level->auto_count_gems = level_mm->auto_count_kettles;
5266
5267   level->mm_laser_red   = level_mm->mm_laser_red;
5268   level->mm_laser_green = level_mm->mm_laser_green;
5269   level->mm_laser_blue  = level_mm->mm_laser_blue;
5270
5271   level->df_laser_red   = level_mm->df_laser_red;
5272   level->df_laser_green = level_mm->df_laser_green;
5273   level->df_laser_blue  = level_mm->df_laser_blue;
5274
5275   strcpy(level->name, level_mm->name);
5276
5277   // only overwrite author from 'levelinfo.conf' if author defined in level
5278   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5279     strcpy(level->author, level_mm->author);
5280
5281   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5282   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5283   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5284   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5285   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5286
5287   level->amoeba_speed  = level_mm->amoeba_speed;
5288   level->mm_time_fuse  = level_mm->time_fuse;
5289   level->mm_time_bomb  = level_mm->time_bomb;
5290   level->mm_time_ball  = level_mm->time_ball;
5291   level->mm_time_block = level_mm->time_block;
5292
5293   level->num_mm_ball_contents = level_mm->num_ball_contents;
5294   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5295   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5296   level->explode_mm_ball = level_mm->explode_ball;
5297
5298   for (i = 0; i < level->num_mm_ball_contents; i++)
5299     level->mm_ball_content[i] =
5300       map_element_MM_to_RND(level_mm->ball_content[i]);
5301
5302   for (x = 0; x < level->fieldx; x++)
5303     for (y = 0; y < level->fieldy; y++)
5304       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5305 }
5306
5307
5308 // ----------------------------------------------------------------------------
5309 // functions for loading DC level
5310 // ----------------------------------------------------------------------------
5311
5312 #define DC_LEVEL_HEADER_SIZE            344
5313
5314 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5315                                         boolean init)
5316 {
5317   static int last_data_encoded;
5318   static int offset1;
5319   static int offset2;
5320   int diff;
5321   int diff_hi, diff_lo;
5322   int data_hi, data_lo;
5323   unsigned short data_decoded;
5324
5325   if (init)
5326   {
5327     last_data_encoded = 0;
5328     offset1 = -1;
5329     offset2 = 0;
5330
5331     return 0;
5332   }
5333
5334   diff = data_encoded - last_data_encoded;
5335   diff_hi = diff & ~0xff;
5336   diff_lo = diff &  0xff;
5337
5338   offset2 += diff_lo;
5339
5340   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5341   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5342   data_hi = data_hi & 0xff00;
5343
5344   data_decoded = data_hi | data_lo;
5345
5346   last_data_encoded = data_encoded;
5347
5348   offset1 = (offset1 + 1) % 31;
5349   offset2 = offset2 & 0xff;
5350
5351   return data_decoded;
5352 }
5353
5354 static int getMappedElement_DC(int element)
5355 {
5356   switch (element)
5357   {
5358     case 0x0000:
5359       element = EL_ROCK;
5360       break;
5361
5362       // 0x0117 - 0x036e: (?)
5363       // EL_DIAMOND
5364
5365       // 0x042d - 0x0684: (?)
5366       // EL_EMERALD
5367
5368     case 0x06f1:
5369       element = EL_NUT;
5370       break;
5371
5372     case 0x074c:
5373       element = EL_BOMB;
5374       break;
5375
5376     case 0x07a4:
5377       element = EL_PEARL;
5378       break;
5379
5380     case 0x0823:
5381       element = EL_CRYSTAL;
5382       break;
5383
5384     case 0x0e77:        // quicksand (boulder)
5385       element = EL_QUICKSAND_FAST_FULL;
5386       break;
5387
5388     case 0x0e99:        // slow quicksand (boulder)
5389       element = EL_QUICKSAND_FULL;
5390       break;
5391
5392     case 0x0ed2:
5393       element = EL_EM_EXIT_OPEN;
5394       break;
5395
5396     case 0x0ee3:
5397       element = EL_EM_EXIT_CLOSED;
5398       break;
5399
5400     case 0x0eeb:
5401       element = EL_EM_STEEL_EXIT_OPEN;
5402       break;
5403
5404     case 0x0efc:
5405       element = EL_EM_STEEL_EXIT_CLOSED;
5406       break;
5407
5408     case 0x0f4f:        // dynamite (lit 1)
5409       element = EL_EM_DYNAMITE_ACTIVE;
5410       break;
5411
5412     case 0x0f57:        // dynamite (lit 2)
5413       element = EL_EM_DYNAMITE_ACTIVE;
5414       break;
5415
5416     case 0x0f5f:        // dynamite (lit 3)
5417       element = EL_EM_DYNAMITE_ACTIVE;
5418       break;
5419
5420     case 0x0f67:        // dynamite (lit 4)
5421       element = EL_EM_DYNAMITE_ACTIVE;
5422       break;
5423
5424     case 0x0f81:
5425     case 0x0f82:
5426     case 0x0f83:
5427     case 0x0f84:
5428       element = EL_AMOEBA_WET;
5429       break;
5430
5431     case 0x0f85:
5432       element = EL_AMOEBA_DROP;
5433       break;
5434
5435     case 0x0fb9:
5436       element = EL_DC_MAGIC_WALL;
5437       break;
5438
5439     case 0x0fd0:
5440       element = EL_SPACESHIP_UP;
5441       break;
5442
5443     case 0x0fd9:
5444       element = EL_SPACESHIP_DOWN;
5445       break;
5446
5447     case 0x0ff1:
5448       element = EL_SPACESHIP_LEFT;
5449       break;
5450
5451     case 0x0ff9:
5452       element = EL_SPACESHIP_RIGHT;
5453       break;
5454
5455     case 0x1057:
5456       element = EL_BUG_UP;
5457       break;
5458
5459     case 0x1060:
5460       element = EL_BUG_DOWN;
5461       break;
5462
5463     case 0x1078:
5464       element = EL_BUG_LEFT;
5465       break;
5466
5467     case 0x1080:
5468       element = EL_BUG_RIGHT;
5469       break;
5470
5471     case 0x10de:
5472       element = EL_MOLE_UP;
5473       break;
5474
5475     case 0x10e7:
5476       element = EL_MOLE_DOWN;
5477       break;
5478
5479     case 0x10ff:
5480       element = EL_MOLE_LEFT;
5481       break;
5482
5483     case 0x1107:
5484       element = EL_MOLE_RIGHT;
5485       break;
5486
5487     case 0x11c0:
5488       element = EL_ROBOT;
5489       break;
5490
5491     case 0x13f5:
5492       element = EL_YAMYAM_UP;
5493       break;
5494
5495     case 0x1425:
5496       element = EL_SWITCHGATE_OPEN;
5497       break;
5498
5499     case 0x1426:
5500       element = EL_SWITCHGATE_CLOSED;
5501       break;
5502
5503     case 0x1437:
5504       element = EL_DC_SWITCHGATE_SWITCH_UP;
5505       break;
5506
5507     case 0x143a:
5508       element = EL_TIMEGATE_CLOSED;
5509       break;
5510
5511     case 0x144c:        // conveyor belt switch (green)
5512       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5513       break;
5514
5515     case 0x144f:        // conveyor belt switch (red)
5516       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5517       break;
5518
5519     case 0x1452:        // conveyor belt switch (blue)
5520       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5521       break;
5522
5523     case 0x145b:
5524       element = EL_CONVEYOR_BELT_3_MIDDLE;
5525       break;
5526
5527     case 0x1463:
5528       element = EL_CONVEYOR_BELT_3_LEFT;
5529       break;
5530
5531     case 0x146b:
5532       element = EL_CONVEYOR_BELT_3_RIGHT;
5533       break;
5534
5535     case 0x1473:
5536       element = EL_CONVEYOR_BELT_1_MIDDLE;
5537       break;
5538
5539     case 0x147b:
5540       element = EL_CONVEYOR_BELT_1_LEFT;
5541       break;
5542
5543     case 0x1483:
5544       element = EL_CONVEYOR_BELT_1_RIGHT;
5545       break;
5546
5547     case 0x148b:
5548       element = EL_CONVEYOR_BELT_4_MIDDLE;
5549       break;
5550
5551     case 0x1493:
5552       element = EL_CONVEYOR_BELT_4_LEFT;
5553       break;
5554
5555     case 0x149b:
5556       element = EL_CONVEYOR_BELT_4_RIGHT;
5557       break;
5558
5559     case 0x14ac:
5560       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5561       break;
5562
5563     case 0x14bd:
5564       element = EL_EXPANDABLE_WALL_VERTICAL;
5565       break;
5566
5567     case 0x14c6:
5568       element = EL_EXPANDABLE_WALL_ANY;
5569       break;
5570
5571     case 0x14ce:        // growing steel wall (left/right)
5572       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5573       break;
5574
5575     case 0x14df:        // growing steel wall (up/down)
5576       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5577       break;
5578
5579     case 0x14e8:        // growing steel wall (up/down/left/right)
5580       element = EL_EXPANDABLE_STEELWALL_ANY;
5581       break;
5582
5583     case 0x14e9:
5584       element = EL_SHIELD_DEADLY;
5585       break;
5586
5587     case 0x1501:
5588       element = EL_EXTRA_TIME;
5589       break;
5590
5591     case 0x154f:
5592       element = EL_ACID;
5593       break;
5594
5595     case 0x1577:
5596       element = EL_EMPTY_SPACE;
5597       break;
5598
5599     case 0x1578:        // quicksand (empty)
5600       element = EL_QUICKSAND_FAST_EMPTY;
5601       break;
5602
5603     case 0x1579:        // slow quicksand (empty)
5604       element = EL_QUICKSAND_EMPTY;
5605       break;
5606
5607       // 0x157c - 0x158b:
5608       // EL_SAND
5609
5610       // 0x1590 - 0x159f:
5611       // EL_DC_LANDMINE
5612
5613     case 0x15a0:
5614       element = EL_EM_DYNAMITE;
5615       break;
5616
5617     case 0x15a1:        // key (red)
5618       element = EL_EM_KEY_1;
5619       break;
5620
5621     case 0x15a2:        // key (yellow)
5622       element = EL_EM_KEY_2;
5623       break;
5624
5625     case 0x15a3:        // key (blue)
5626       element = EL_EM_KEY_4;
5627       break;
5628
5629     case 0x15a4:        // key (green)
5630       element = EL_EM_KEY_3;
5631       break;
5632
5633     case 0x15a5:        // key (white)
5634       element = EL_DC_KEY_WHITE;
5635       break;
5636
5637     case 0x15a6:
5638       element = EL_WALL_SLIPPERY;
5639       break;
5640
5641     case 0x15a7:
5642       element = EL_WALL;
5643       break;
5644
5645     case 0x15a8:        // wall (not round)
5646       element = EL_WALL;
5647       break;
5648
5649     case 0x15a9:        // (blue)
5650       element = EL_CHAR_A;
5651       break;
5652
5653     case 0x15aa:        // (blue)
5654       element = EL_CHAR_B;
5655       break;
5656
5657     case 0x15ab:        // (blue)
5658       element = EL_CHAR_C;
5659       break;
5660
5661     case 0x15ac:        // (blue)
5662       element = EL_CHAR_D;
5663       break;
5664
5665     case 0x15ad:        // (blue)
5666       element = EL_CHAR_E;
5667       break;
5668
5669     case 0x15ae:        // (blue)
5670       element = EL_CHAR_F;
5671       break;
5672
5673     case 0x15af:        // (blue)
5674       element = EL_CHAR_G;
5675       break;
5676
5677     case 0x15b0:        // (blue)
5678       element = EL_CHAR_H;
5679       break;
5680
5681     case 0x15b1:        // (blue)
5682       element = EL_CHAR_I;
5683       break;
5684
5685     case 0x15b2:        // (blue)
5686       element = EL_CHAR_J;
5687       break;
5688
5689     case 0x15b3:        // (blue)
5690       element = EL_CHAR_K;
5691       break;
5692
5693     case 0x15b4:        // (blue)
5694       element = EL_CHAR_L;
5695       break;
5696
5697     case 0x15b5:        // (blue)
5698       element = EL_CHAR_M;
5699       break;
5700
5701     case 0x15b6:        // (blue)
5702       element = EL_CHAR_N;
5703       break;
5704
5705     case 0x15b7:        // (blue)
5706       element = EL_CHAR_O;
5707       break;
5708
5709     case 0x15b8:        // (blue)
5710       element = EL_CHAR_P;
5711       break;
5712
5713     case 0x15b9:        // (blue)
5714       element = EL_CHAR_Q;
5715       break;
5716
5717     case 0x15ba:        // (blue)
5718       element = EL_CHAR_R;
5719       break;
5720
5721     case 0x15bb:        // (blue)
5722       element = EL_CHAR_S;
5723       break;
5724
5725     case 0x15bc:        // (blue)
5726       element = EL_CHAR_T;
5727       break;
5728
5729     case 0x15bd:        // (blue)
5730       element = EL_CHAR_U;
5731       break;
5732
5733     case 0x15be:        // (blue)
5734       element = EL_CHAR_V;
5735       break;
5736
5737     case 0x15bf:        // (blue)
5738       element = EL_CHAR_W;
5739       break;
5740
5741     case 0x15c0:        // (blue)
5742       element = EL_CHAR_X;
5743       break;
5744
5745     case 0x15c1:        // (blue)
5746       element = EL_CHAR_Y;
5747       break;
5748
5749     case 0x15c2:        // (blue)
5750       element = EL_CHAR_Z;
5751       break;
5752
5753     case 0x15c3:        // (blue)
5754       element = EL_CHAR_AUMLAUT;
5755       break;
5756
5757     case 0x15c4:        // (blue)
5758       element = EL_CHAR_OUMLAUT;
5759       break;
5760
5761     case 0x15c5:        // (blue)
5762       element = EL_CHAR_UUMLAUT;
5763       break;
5764
5765     case 0x15c6:        // (blue)
5766       element = EL_CHAR_0;
5767       break;
5768
5769     case 0x15c7:        // (blue)
5770       element = EL_CHAR_1;
5771       break;
5772
5773     case 0x15c8:        // (blue)
5774       element = EL_CHAR_2;
5775       break;
5776
5777     case 0x15c9:        // (blue)
5778       element = EL_CHAR_3;
5779       break;
5780
5781     case 0x15ca:        // (blue)
5782       element = EL_CHAR_4;
5783       break;
5784
5785     case 0x15cb:        // (blue)
5786       element = EL_CHAR_5;
5787       break;
5788
5789     case 0x15cc:        // (blue)
5790       element = EL_CHAR_6;
5791       break;
5792
5793     case 0x15cd:        // (blue)
5794       element = EL_CHAR_7;
5795       break;
5796
5797     case 0x15ce:        // (blue)
5798       element = EL_CHAR_8;
5799       break;
5800
5801     case 0x15cf:        // (blue)
5802       element = EL_CHAR_9;
5803       break;
5804
5805     case 0x15d0:        // (blue)
5806       element = EL_CHAR_PERIOD;
5807       break;
5808
5809     case 0x15d1:        // (blue)
5810       element = EL_CHAR_EXCLAM;
5811       break;
5812
5813     case 0x15d2:        // (blue)
5814       element = EL_CHAR_COLON;
5815       break;
5816
5817     case 0x15d3:        // (blue)
5818       element = EL_CHAR_LESS;
5819       break;
5820
5821     case 0x15d4:        // (blue)
5822       element = EL_CHAR_GREATER;
5823       break;
5824
5825     case 0x15d5:        // (blue)
5826       element = EL_CHAR_QUESTION;
5827       break;
5828
5829     case 0x15d6:        // (blue)
5830       element = EL_CHAR_COPYRIGHT;
5831       break;
5832
5833     case 0x15d7:        // (blue)
5834       element = EL_CHAR_UP;
5835       break;
5836
5837     case 0x15d8:        // (blue)
5838       element = EL_CHAR_DOWN;
5839       break;
5840
5841     case 0x15d9:        // (blue)
5842       element = EL_CHAR_BUTTON;
5843       break;
5844
5845     case 0x15da:        // (blue)
5846       element = EL_CHAR_PLUS;
5847       break;
5848
5849     case 0x15db:        // (blue)
5850       element = EL_CHAR_MINUS;
5851       break;
5852
5853     case 0x15dc:        // (blue)
5854       element = EL_CHAR_APOSTROPHE;
5855       break;
5856
5857     case 0x15dd:        // (blue)
5858       element = EL_CHAR_PARENLEFT;
5859       break;
5860
5861     case 0x15de:        // (blue)
5862       element = EL_CHAR_PARENRIGHT;
5863       break;
5864
5865     case 0x15df:        // (green)
5866       element = EL_CHAR_A;
5867       break;
5868
5869     case 0x15e0:        // (green)
5870       element = EL_CHAR_B;
5871       break;
5872
5873     case 0x15e1:        // (green)
5874       element = EL_CHAR_C;
5875       break;
5876
5877     case 0x15e2:        // (green)
5878       element = EL_CHAR_D;
5879       break;
5880
5881     case 0x15e3:        // (green)
5882       element = EL_CHAR_E;
5883       break;
5884
5885     case 0x15e4:        // (green)
5886       element = EL_CHAR_F;
5887       break;
5888
5889     case 0x15e5:        // (green)
5890       element = EL_CHAR_G;
5891       break;
5892
5893     case 0x15e6:        // (green)
5894       element = EL_CHAR_H;
5895       break;
5896
5897     case 0x15e7:        // (green)
5898       element = EL_CHAR_I;
5899       break;
5900
5901     case 0x15e8:        // (green)
5902       element = EL_CHAR_J;
5903       break;
5904
5905     case 0x15e9:        // (green)
5906       element = EL_CHAR_K;
5907       break;
5908
5909     case 0x15ea:        // (green)
5910       element = EL_CHAR_L;
5911       break;
5912
5913     case 0x15eb:        // (green)
5914       element = EL_CHAR_M;
5915       break;
5916
5917     case 0x15ec:        // (green)
5918       element = EL_CHAR_N;
5919       break;
5920
5921     case 0x15ed:        // (green)
5922       element = EL_CHAR_O;
5923       break;
5924
5925     case 0x15ee:        // (green)
5926       element = EL_CHAR_P;
5927       break;
5928
5929     case 0x15ef:        // (green)
5930       element = EL_CHAR_Q;
5931       break;
5932
5933     case 0x15f0:        // (green)
5934       element = EL_CHAR_R;
5935       break;
5936
5937     case 0x15f1:        // (green)
5938       element = EL_CHAR_S;
5939       break;
5940
5941     case 0x15f2:        // (green)
5942       element = EL_CHAR_T;
5943       break;
5944
5945     case 0x15f3:        // (green)
5946       element = EL_CHAR_U;
5947       break;
5948
5949     case 0x15f4:        // (green)
5950       element = EL_CHAR_V;
5951       break;
5952
5953     case 0x15f5:        // (green)
5954       element = EL_CHAR_W;
5955       break;
5956
5957     case 0x15f6:        // (green)
5958       element = EL_CHAR_X;
5959       break;
5960
5961     case 0x15f7:        // (green)
5962       element = EL_CHAR_Y;
5963       break;
5964
5965     case 0x15f8:        // (green)
5966       element = EL_CHAR_Z;
5967       break;
5968
5969     case 0x15f9:        // (green)
5970       element = EL_CHAR_AUMLAUT;
5971       break;
5972
5973     case 0x15fa:        // (green)
5974       element = EL_CHAR_OUMLAUT;
5975       break;
5976
5977     case 0x15fb:        // (green)
5978       element = EL_CHAR_UUMLAUT;
5979       break;
5980
5981     case 0x15fc:        // (green)
5982       element = EL_CHAR_0;
5983       break;
5984
5985     case 0x15fd:        // (green)
5986       element = EL_CHAR_1;
5987       break;
5988
5989     case 0x15fe:        // (green)
5990       element = EL_CHAR_2;
5991       break;
5992
5993     case 0x15ff:        // (green)
5994       element = EL_CHAR_3;
5995       break;
5996
5997     case 0x1600:        // (green)
5998       element = EL_CHAR_4;
5999       break;
6000
6001     case 0x1601:        // (green)
6002       element = EL_CHAR_5;
6003       break;
6004
6005     case 0x1602:        // (green)
6006       element = EL_CHAR_6;
6007       break;
6008
6009     case 0x1603:        // (green)
6010       element = EL_CHAR_7;
6011       break;
6012
6013     case 0x1604:        // (green)
6014       element = EL_CHAR_8;
6015       break;
6016
6017     case 0x1605:        // (green)
6018       element = EL_CHAR_9;
6019       break;
6020
6021     case 0x1606:        // (green)
6022       element = EL_CHAR_PERIOD;
6023       break;
6024
6025     case 0x1607:        // (green)
6026       element = EL_CHAR_EXCLAM;
6027       break;
6028
6029     case 0x1608:        // (green)
6030       element = EL_CHAR_COLON;
6031       break;
6032
6033     case 0x1609:        // (green)
6034       element = EL_CHAR_LESS;
6035       break;
6036
6037     case 0x160a:        // (green)
6038       element = EL_CHAR_GREATER;
6039       break;
6040
6041     case 0x160b:        // (green)
6042       element = EL_CHAR_QUESTION;
6043       break;
6044
6045     case 0x160c:        // (green)
6046       element = EL_CHAR_COPYRIGHT;
6047       break;
6048
6049     case 0x160d:        // (green)
6050       element = EL_CHAR_UP;
6051       break;
6052
6053     case 0x160e:        // (green)
6054       element = EL_CHAR_DOWN;
6055       break;
6056
6057     case 0x160f:        // (green)
6058       element = EL_CHAR_BUTTON;
6059       break;
6060
6061     case 0x1610:        // (green)
6062       element = EL_CHAR_PLUS;
6063       break;
6064
6065     case 0x1611:        // (green)
6066       element = EL_CHAR_MINUS;
6067       break;
6068
6069     case 0x1612:        // (green)
6070       element = EL_CHAR_APOSTROPHE;
6071       break;
6072
6073     case 0x1613:        // (green)
6074       element = EL_CHAR_PARENLEFT;
6075       break;
6076
6077     case 0x1614:        // (green)
6078       element = EL_CHAR_PARENRIGHT;
6079       break;
6080
6081     case 0x1615:        // (blue steel)
6082       element = EL_STEEL_CHAR_A;
6083       break;
6084
6085     case 0x1616:        // (blue steel)
6086       element = EL_STEEL_CHAR_B;
6087       break;
6088
6089     case 0x1617:        // (blue steel)
6090       element = EL_STEEL_CHAR_C;
6091       break;
6092
6093     case 0x1618:        // (blue steel)
6094       element = EL_STEEL_CHAR_D;
6095       break;
6096
6097     case 0x1619:        // (blue steel)
6098       element = EL_STEEL_CHAR_E;
6099       break;
6100
6101     case 0x161a:        // (blue steel)
6102       element = EL_STEEL_CHAR_F;
6103       break;
6104
6105     case 0x161b:        // (blue steel)
6106       element = EL_STEEL_CHAR_G;
6107       break;
6108
6109     case 0x161c:        // (blue steel)
6110       element = EL_STEEL_CHAR_H;
6111       break;
6112
6113     case 0x161d:        // (blue steel)
6114       element = EL_STEEL_CHAR_I;
6115       break;
6116
6117     case 0x161e:        // (blue steel)
6118       element = EL_STEEL_CHAR_J;
6119       break;
6120
6121     case 0x161f:        // (blue steel)
6122       element = EL_STEEL_CHAR_K;
6123       break;
6124
6125     case 0x1620:        // (blue steel)
6126       element = EL_STEEL_CHAR_L;
6127       break;
6128
6129     case 0x1621:        // (blue steel)
6130       element = EL_STEEL_CHAR_M;
6131       break;
6132
6133     case 0x1622:        // (blue steel)
6134       element = EL_STEEL_CHAR_N;
6135       break;
6136
6137     case 0x1623:        // (blue steel)
6138       element = EL_STEEL_CHAR_O;
6139       break;
6140
6141     case 0x1624:        // (blue steel)
6142       element = EL_STEEL_CHAR_P;
6143       break;
6144
6145     case 0x1625:        // (blue steel)
6146       element = EL_STEEL_CHAR_Q;
6147       break;
6148
6149     case 0x1626:        // (blue steel)
6150       element = EL_STEEL_CHAR_R;
6151       break;
6152
6153     case 0x1627:        // (blue steel)
6154       element = EL_STEEL_CHAR_S;
6155       break;
6156
6157     case 0x1628:        // (blue steel)
6158       element = EL_STEEL_CHAR_T;
6159       break;
6160
6161     case 0x1629:        // (blue steel)
6162       element = EL_STEEL_CHAR_U;
6163       break;
6164
6165     case 0x162a:        // (blue steel)
6166       element = EL_STEEL_CHAR_V;
6167       break;
6168
6169     case 0x162b:        // (blue steel)
6170       element = EL_STEEL_CHAR_W;
6171       break;
6172
6173     case 0x162c:        // (blue steel)
6174       element = EL_STEEL_CHAR_X;
6175       break;
6176
6177     case 0x162d:        // (blue steel)
6178       element = EL_STEEL_CHAR_Y;
6179       break;
6180
6181     case 0x162e:        // (blue steel)
6182       element = EL_STEEL_CHAR_Z;
6183       break;
6184
6185     case 0x162f:        // (blue steel)
6186       element = EL_STEEL_CHAR_AUMLAUT;
6187       break;
6188
6189     case 0x1630:        // (blue steel)
6190       element = EL_STEEL_CHAR_OUMLAUT;
6191       break;
6192
6193     case 0x1631:        // (blue steel)
6194       element = EL_STEEL_CHAR_UUMLAUT;
6195       break;
6196
6197     case 0x1632:        // (blue steel)
6198       element = EL_STEEL_CHAR_0;
6199       break;
6200
6201     case 0x1633:        // (blue steel)
6202       element = EL_STEEL_CHAR_1;
6203       break;
6204
6205     case 0x1634:        // (blue steel)
6206       element = EL_STEEL_CHAR_2;
6207       break;
6208
6209     case 0x1635:        // (blue steel)
6210       element = EL_STEEL_CHAR_3;
6211       break;
6212
6213     case 0x1636:        // (blue steel)
6214       element = EL_STEEL_CHAR_4;
6215       break;
6216
6217     case 0x1637:        // (blue steel)
6218       element = EL_STEEL_CHAR_5;
6219       break;
6220
6221     case 0x1638:        // (blue steel)
6222       element = EL_STEEL_CHAR_6;
6223       break;
6224
6225     case 0x1639:        // (blue steel)
6226       element = EL_STEEL_CHAR_7;
6227       break;
6228
6229     case 0x163a:        // (blue steel)
6230       element = EL_STEEL_CHAR_8;
6231       break;
6232
6233     case 0x163b:        // (blue steel)
6234       element = EL_STEEL_CHAR_9;
6235       break;
6236
6237     case 0x163c:        // (blue steel)
6238       element = EL_STEEL_CHAR_PERIOD;
6239       break;
6240
6241     case 0x163d:        // (blue steel)
6242       element = EL_STEEL_CHAR_EXCLAM;
6243       break;
6244
6245     case 0x163e:        // (blue steel)
6246       element = EL_STEEL_CHAR_COLON;
6247       break;
6248
6249     case 0x163f:        // (blue steel)
6250       element = EL_STEEL_CHAR_LESS;
6251       break;
6252
6253     case 0x1640:        // (blue steel)
6254       element = EL_STEEL_CHAR_GREATER;
6255       break;
6256
6257     case 0x1641:        // (blue steel)
6258       element = EL_STEEL_CHAR_QUESTION;
6259       break;
6260
6261     case 0x1642:        // (blue steel)
6262       element = EL_STEEL_CHAR_COPYRIGHT;
6263       break;
6264
6265     case 0x1643:        // (blue steel)
6266       element = EL_STEEL_CHAR_UP;
6267       break;
6268
6269     case 0x1644:        // (blue steel)
6270       element = EL_STEEL_CHAR_DOWN;
6271       break;
6272
6273     case 0x1645:        // (blue steel)
6274       element = EL_STEEL_CHAR_BUTTON;
6275       break;
6276
6277     case 0x1646:        // (blue steel)
6278       element = EL_STEEL_CHAR_PLUS;
6279       break;
6280
6281     case 0x1647:        // (blue steel)
6282       element = EL_STEEL_CHAR_MINUS;
6283       break;
6284
6285     case 0x1648:        // (blue steel)
6286       element = EL_STEEL_CHAR_APOSTROPHE;
6287       break;
6288
6289     case 0x1649:        // (blue steel)
6290       element = EL_STEEL_CHAR_PARENLEFT;
6291       break;
6292
6293     case 0x164a:        // (blue steel)
6294       element = EL_STEEL_CHAR_PARENRIGHT;
6295       break;
6296
6297     case 0x164b:        // (green steel)
6298       element = EL_STEEL_CHAR_A;
6299       break;
6300
6301     case 0x164c:        // (green steel)
6302       element = EL_STEEL_CHAR_B;
6303       break;
6304
6305     case 0x164d:        // (green steel)
6306       element = EL_STEEL_CHAR_C;
6307       break;
6308
6309     case 0x164e:        // (green steel)
6310       element = EL_STEEL_CHAR_D;
6311       break;
6312
6313     case 0x164f:        // (green steel)
6314       element = EL_STEEL_CHAR_E;
6315       break;
6316
6317     case 0x1650:        // (green steel)
6318       element = EL_STEEL_CHAR_F;
6319       break;
6320
6321     case 0x1651:        // (green steel)
6322       element = EL_STEEL_CHAR_G;
6323       break;
6324
6325     case 0x1652:        // (green steel)
6326       element = EL_STEEL_CHAR_H;
6327       break;
6328
6329     case 0x1653:        // (green steel)
6330       element = EL_STEEL_CHAR_I;
6331       break;
6332
6333     case 0x1654:        // (green steel)
6334       element = EL_STEEL_CHAR_J;
6335       break;
6336
6337     case 0x1655:        // (green steel)
6338       element = EL_STEEL_CHAR_K;
6339       break;
6340
6341     case 0x1656:        // (green steel)
6342       element = EL_STEEL_CHAR_L;
6343       break;
6344
6345     case 0x1657:        // (green steel)
6346       element = EL_STEEL_CHAR_M;
6347       break;
6348
6349     case 0x1658:        // (green steel)
6350       element = EL_STEEL_CHAR_N;
6351       break;
6352
6353     case 0x1659:        // (green steel)
6354       element = EL_STEEL_CHAR_O;
6355       break;
6356
6357     case 0x165a:        // (green steel)
6358       element = EL_STEEL_CHAR_P;
6359       break;
6360
6361     case 0x165b:        // (green steel)
6362       element = EL_STEEL_CHAR_Q;
6363       break;
6364
6365     case 0x165c:        // (green steel)
6366       element = EL_STEEL_CHAR_R;
6367       break;
6368
6369     case 0x165d:        // (green steel)
6370       element = EL_STEEL_CHAR_S;
6371       break;
6372
6373     case 0x165e:        // (green steel)
6374       element = EL_STEEL_CHAR_T;
6375       break;
6376
6377     case 0x165f:        // (green steel)
6378       element = EL_STEEL_CHAR_U;
6379       break;
6380
6381     case 0x1660:        // (green steel)
6382       element = EL_STEEL_CHAR_V;
6383       break;
6384
6385     case 0x1661:        // (green steel)
6386       element = EL_STEEL_CHAR_W;
6387       break;
6388
6389     case 0x1662:        // (green steel)
6390       element = EL_STEEL_CHAR_X;
6391       break;
6392
6393     case 0x1663:        // (green steel)
6394       element = EL_STEEL_CHAR_Y;
6395       break;
6396
6397     case 0x1664:        // (green steel)
6398       element = EL_STEEL_CHAR_Z;
6399       break;
6400
6401     case 0x1665:        // (green steel)
6402       element = EL_STEEL_CHAR_AUMLAUT;
6403       break;
6404
6405     case 0x1666:        // (green steel)
6406       element = EL_STEEL_CHAR_OUMLAUT;
6407       break;
6408
6409     case 0x1667:        // (green steel)
6410       element = EL_STEEL_CHAR_UUMLAUT;
6411       break;
6412
6413     case 0x1668:        // (green steel)
6414       element = EL_STEEL_CHAR_0;
6415       break;
6416
6417     case 0x1669:        // (green steel)
6418       element = EL_STEEL_CHAR_1;
6419       break;
6420
6421     case 0x166a:        // (green steel)
6422       element = EL_STEEL_CHAR_2;
6423       break;
6424
6425     case 0x166b:        // (green steel)
6426       element = EL_STEEL_CHAR_3;
6427       break;
6428
6429     case 0x166c:        // (green steel)
6430       element = EL_STEEL_CHAR_4;
6431       break;
6432
6433     case 0x166d:        // (green steel)
6434       element = EL_STEEL_CHAR_5;
6435       break;
6436
6437     case 0x166e:        // (green steel)
6438       element = EL_STEEL_CHAR_6;
6439       break;
6440
6441     case 0x166f:        // (green steel)
6442       element = EL_STEEL_CHAR_7;
6443       break;
6444
6445     case 0x1670:        // (green steel)
6446       element = EL_STEEL_CHAR_8;
6447       break;
6448
6449     case 0x1671:        // (green steel)
6450       element = EL_STEEL_CHAR_9;
6451       break;
6452
6453     case 0x1672:        // (green steel)
6454       element = EL_STEEL_CHAR_PERIOD;
6455       break;
6456
6457     case 0x1673:        // (green steel)
6458       element = EL_STEEL_CHAR_EXCLAM;
6459       break;
6460
6461     case 0x1674:        // (green steel)
6462       element = EL_STEEL_CHAR_COLON;
6463       break;
6464
6465     case 0x1675:        // (green steel)
6466       element = EL_STEEL_CHAR_LESS;
6467       break;
6468
6469     case 0x1676:        // (green steel)
6470       element = EL_STEEL_CHAR_GREATER;
6471       break;
6472
6473     case 0x1677:        // (green steel)
6474       element = EL_STEEL_CHAR_QUESTION;
6475       break;
6476
6477     case 0x1678:        // (green steel)
6478       element = EL_STEEL_CHAR_COPYRIGHT;
6479       break;
6480
6481     case 0x1679:        // (green steel)
6482       element = EL_STEEL_CHAR_UP;
6483       break;
6484
6485     case 0x167a:        // (green steel)
6486       element = EL_STEEL_CHAR_DOWN;
6487       break;
6488
6489     case 0x167b:        // (green steel)
6490       element = EL_STEEL_CHAR_BUTTON;
6491       break;
6492
6493     case 0x167c:        // (green steel)
6494       element = EL_STEEL_CHAR_PLUS;
6495       break;
6496
6497     case 0x167d:        // (green steel)
6498       element = EL_STEEL_CHAR_MINUS;
6499       break;
6500
6501     case 0x167e:        // (green steel)
6502       element = EL_STEEL_CHAR_APOSTROPHE;
6503       break;
6504
6505     case 0x167f:        // (green steel)
6506       element = EL_STEEL_CHAR_PARENLEFT;
6507       break;
6508
6509     case 0x1680:        // (green steel)
6510       element = EL_STEEL_CHAR_PARENRIGHT;
6511       break;
6512
6513     case 0x1681:        // gate (red)
6514       element = EL_EM_GATE_1;
6515       break;
6516
6517     case 0x1682:        // secret gate (red)
6518       element = EL_EM_GATE_1_GRAY;
6519       break;
6520
6521     case 0x1683:        // gate (yellow)
6522       element = EL_EM_GATE_2;
6523       break;
6524
6525     case 0x1684:        // secret gate (yellow)
6526       element = EL_EM_GATE_2_GRAY;
6527       break;
6528
6529     case 0x1685:        // gate (blue)
6530       element = EL_EM_GATE_4;
6531       break;
6532
6533     case 0x1686:        // secret gate (blue)
6534       element = EL_EM_GATE_4_GRAY;
6535       break;
6536
6537     case 0x1687:        // gate (green)
6538       element = EL_EM_GATE_3;
6539       break;
6540
6541     case 0x1688:        // secret gate (green)
6542       element = EL_EM_GATE_3_GRAY;
6543       break;
6544
6545     case 0x1689:        // gate (white)
6546       element = EL_DC_GATE_WHITE;
6547       break;
6548
6549     case 0x168a:        // secret gate (white)
6550       element = EL_DC_GATE_WHITE_GRAY;
6551       break;
6552
6553     case 0x168b:        // secret gate (no key)
6554       element = EL_DC_GATE_FAKE_GRAY;
6555       break;
6556
6557     case 0x168c:
6558       element = EL_ROBOT_WHEEL;
6559       break;
6560
6561     case 0x168d:
6562       element = EL_DC_TIMEGATE_SWITCH;
6563       break;
6564
6565     case 0x168e:
6566       element = EL_ACID_POOL_BOTTOM;
6567       break;
6568
6569     case 0x168f:
6570       element = EL_ACID_POOL_TOPLEFT;
6571       break;
6572
6573     case 0x1690:
6574       element = EL_ACID_POOL_TOPRIGHT;
6575       break;
6576
6577     case 0x1691:
6578       element = EL_ACID_POOL_BOTTOMLEFT;
6579       break;
6580
6581     case 0x1692:
6582       element = EL_ACID_POOL_BOTTOMRIGHT;
6583       break;
6584
6585     case 0x1693:
6586       element = EL_STEELWALL;
6587       break;
6588
6589     case 0x1694:
6590       element = EL_STEELWALL_SLIPPERY;
6591       break;
6592
6593     case 0x1695:        // steel wall (not round)
6594       element = EL_STEELWALL;
6595       break;
6596
6597     case 0x1696:        // steel wall (left)
6598       element = EL_DC_STEELWALL_1_LEFT;
6599       break;
6600
6601     case 0x1697:        // steel wall (bottom)
6602       element = EL_DC_STEELWALL_1_BOTTOM;
6603       break;
6604
6605     case 0x1698:        // steel wall (right)
6606       element = EL_DC_STEELWALL_1_RIGHT;
6607       break;
6608
6609     case 0x1699:        // steel wall (top)
6610       element = EL_DC_STEELWALL_1_TOP;
6611       break;
6612
6613     case 0x169a:        // steel wall (left/bottom)
6614       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6615       break;
6616
6617     case 0x169b:        // steel wall (right/bottom)
6618       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6619       break;
6620
6621     case 0x169c:        // steel wall (right/top)
6622       element = EL_DC_STEELWALL_1_TOPRIGHT;
6623       break;
6624
6625     case 0x169d:        // steel wall (left/top)
6626       element = EL_DC_STEELWALL_1_TOPLEFT;
6627       break;
6628
6629     case 0x169e:        // steel wall (right/bottom small)
6630       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6631       break;
6632
6633     case 0x169f:        // steel wall (left/bottom small)
6634       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6635       break;
6636
6637     case 0x16a0:        // steel wall (right/top small)
6638       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6639       break;
6640
6641     case 0x16a1:        // steel wall (left/top small)
6642       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6643       break;
6644
6645     case 0x16a2:        // steel wall (left/right)
6646       element = EL_DC_STEELWALL_1_VERTICAL;
6647       break;
6648
6649     case 0x16a3:        // steel wall (top/bottom)
6650       element = EL_DC_STEELWALL_1_HORIZONTAL;
6651       break;
6652
6653     case 0x16a4:        // steel wall 2 (left end)
6654       element = EL_DC_STEELWALL_2_LEFT;
6655       break;
6656
6657     case 0x16a5:        // steel wall 2 (right end)
6658       element = EL_DC_STEELWALL_2_RIGHT;
6659       break;
6660
6661     case 0x16a6:        // steel wall 2 (top end)
6662       element = EL_DC_STEELWALL_2_TOP;
6663       break;
6664
6665     case 0x16a7:        // steel wall 2 (bottom end)
6666       element = EL_DC_STEELWALL_2_BOTTOM;
6667       break;
6668
6669     case 0x16a8:        // steel wall 2 (left/right)
6670       element = EL_DC_STEELWALL_2_HORIZONTAL;
6671       break;
6672
6673     case 0x16a9:        // steel wall 2 (up/down)
6674       element = EL_DC_STEELWALL_2_VERTICAL;
6675       break;
6676
6677     case 0x16aa:        // steel wall 2 (mid)
6678       element = EL_DC_STEELWALL_2_MIDDLE;
6679       break;
6680
6681     case 0x16ab:
6682       element = EL_SIGN_EXCLAMATION;
6683       break;
6684
6685     case 0x16ac:
6686       element = EL_SIGN_RADIOACTIVITY;
6687       break;
6688
6689     case 0x16ad:
6690       element = EL_SIGN_STOP;
6691       break;
6692
6693     case 0x16ae:
6694       element = EL_SIGN_WHEELCHAIR;
6695       break;
6696
6697     case 0x16af:
6698       element = EL_SIGN_PARKING;
6699       break;
6700
6701     case 0x16b0:
6702       element = EL_SIGN_NO_ENTRY;
6703       break;
6704
6705     case 0x16b1:
6706       element = EL_SIGN_HEART;
6707       break;
6708
6709     case 0x16b2:
6710       element = EL_SIGN_GIVE_WAY;
6711       break;
6712
6713     case 0x16b3:
6714       element = EL_SIGN_ENTRY_FORBIDDEN;
6715       break;
6716
6717     case 0x16b4:
6718       element = EL_SIGN_EMERGENCY_EXIT;
6719       break;
6720
6721     case 0x16b5:
6722       element = EL_SIGN_YIN_YANG;
6723       break;
6724
6725     case 0x16b6:
6726       element = EL_WALL_EMERALD;
6727       break;
6728
6729     case 0x16b7:
6730       element = EL_WALL_DIAMOND;
6731       break;
6732
6733     case 0x16b8:
6734       element = EL_WALL_PEARL;
6735       break;
6736
6737     case 0x16b9:
6738       element = EL_WALL_CRYSTAL;
6739       break;
6740
6741     case 0x16ba:
6742       element = EL_INVISIBLE_WALL;
6743       break;
6744
6745     case 0x16bb:
6746       element = EL_INVISIBLE_STEELWALL;
6747       break;
6748
6749       // 0x16bc - 0x16cb:
6750       // EL_INVISIBLE_SAND
6751
6752     case 0x16cc:
6753       element = EL_LIGHT_SWITCH;
6754       break;
6755
6756     case 0x16cd:
6757       element = EL_ENVELOPE_1;
6758       break;
6759
6760     default:
6761       if (element >= 0x0117 && element <= 0x036e)       // (?)
6762         element = EL_DIAMOND;
6763       else if (element >= 0x042d && element <= 0x0684)  // (?)
6764         element = EL_EMERALD;
6765       else if (element >= 0x157c && element <= 0x158b)
6766         element = EL_SAND;
6767       else if (element >= 0x1590 && element <= 0x159f)
6768         element = EL_DC_LANDMINE;
6769       else if (element >= 0x16bc && element <= 0x16cb)
6770         element = EL_INVISIBLE_SAND;
6771       else
6772       {
6773         Warn("unknown Diamond Caves element 0x%04x", element);
6774
6775         element = EL_UNKNOWN;
6776       }
6777       break;
6778   }
6779
6780   return getMappedElement(element);
6781 }
6782
6783 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6784 {
6785   byte header[DC_LEVEL_HEADER_SIZE];
6786   int envelope_size;
6787   int envelope_header_pos = 62;
6788   int envelope_content_pos = 94;
6789   int level_name_pos = 251;
6790   int level_author_pos = 292;
6791   int envelope_header_len;
6792   int envelope_content_len;
6793   int level_name_len;
6794   int level_author_len;
6795   int fieldx, fieldy;
6796   int num_yamyam_contents;
6797   int i, x, y;
6798
6799   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6800
6801   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6802   {
6803     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6804
6805     header[i * 2 + 0] = header_word >> 8;
6806     header[i * 2 + 1] = header_word & 0xff;
6807   }
6808
6809   // read some values from level header to check level decoding integrity
6810   fieldx = header[6] | (header[7] << 8);
6811   fieldy = header[8] | (header[9] << 8);
6812   num_yamyam_contents = header[60] | (header[61] << 8);
6813
6814   // do some simple sanity checks to ensure that level was correctly decoded
6815   if (fieldx < 1 || fieldx > 256 ||
6816       fieldy < 1 || fieldy > 256 ||
6817       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6818   {
6819     level->no_valid_file = TRUE;
6820
6821     Warn("cannot decode level from stream -- using empty level");
6822
6823     return;
6824   }
6825
6826   // maximum envelope header size is 31 bytes
6827   envelope_header_len   = header[envelope_header_pos];
6828   // maximum envelope content size is 110 (156?) bytes
6829   envelope_content_len  = header[envelope_content_pos];
6830
6831   // maximum level title size is 40 bytes
6832   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6833   // maximum level author size is 30 (51?) bytes
6834   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6835
6836   envelope_size = 0;
6837
6838   for (i = 0; i < envelope_header_len; i++)
6839     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6840       level->envelope[0].text[envelope_size++] =
6841         header[envelope_header_pos + 1 + i];
6842
6843   if (envelope_header_len > 0 && envelope_content_len > 0)
6844   {
6845     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6846       level->envelope[0].text[envelope_size++] = '\n';
6847     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6848       level->envelope[0].text[envelope_size++] = '\n';
6849   }
6850
6851   for (i = 0; i < envelope_content_len; i++)
6852     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6853       level->envelope[0].text[envelope_size++] =
6854         header[envelope_content_pos + 1 + i];
6855
6856   level->envelope[0].text[envelope_size] = '\0';
6857
6858   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6859   level->envelope[0].ysize = 10;
6860   level->envelope[0].autowrap = TRUE;
6861   level->envelope[0].centered = TRUE;
6862
6863   for (i = 0; i < level_name_len; i++)
6864     level->name[i] = header[level_name_pos + 1 + i];
6865   level->name[level_name_len] = '\0';
6866
6867   for (i = 0; i < level_author_len; i++)
6868     level->author[i] = header[level_author_pos + 1 + i];
6869   level->author[level_author_len] = '\0';
6870
6871   num_yamyam_contents = header[60] | (header[61] << 8);
6872   level->num_yamyam_contents =
6873     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6874
6875   for (i = 0; i < num_yamyam_contents; i++)
6876   {
6877     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6878     {
6879       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6880       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6881
6882       if (i < MAX_ELEMENT_CONTENTS)
6883         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6884     }
6885   }
6886
6887   fieldx = header[6] | (header[7] << 8);
6888   fieldy = header[8] | (header[9] << 8);
6889   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6890   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6891
6892   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6893   {
6894     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6895     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6896
6897     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6898       level->field[x][y] = getMappedElement_DC(element_dc);
6899   }
6900
6901   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6902   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6903   level->field[x][y] = EL_PLAYER_1;
6904
6905   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6906   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6907   level->field[x][y] = EL_PLAYER_2;
6908
6909   level->gems_needed            = header[18] | (header[19] << 8);
6910
6911   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6912   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6913   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6914   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6915   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6916   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6917   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6918   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6919   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6920   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6921   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6922   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6923
6924   level->time                   = header[44] | (header[45] << 8);
6925
6926   level->amoeba_speed           = header[46] | (header[47] << 8);
6927   level->time_light             = header[48] | (header[49] << 8);
6928   level->time_timegate          = header[50] | (header[51] << 8);
6929   level->time_wheel             = header[52] | (header[53] << 8);
6930   level->time_magic_wall        = header[54] | (header[55] << 8);
6931   level->extra_time             = header[56] | (header[57] << 8);
6932   level->shield_normal_time     = header[58] | (header[59] << 8);
6933
6934   // shield and extra time elements do not have a score
6935   level->score[SC_SHIELD]       = 0;
6936   level->extra_time_score       = 0;
6937
6938   // set time for normal and deadly shields to the same value
6939   level->shield_deadly_time     = level->shield_normal_time;
6940
6941   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6942   // can slip down from flat walls, like normal walls and steel walls
6943   level->em_slippery_gems = TRUE;
6944
6945   // time score is counted for each 10 seconds left in Diamond Caves levels
6946   level->time_score_base = 10;
6947 }
6948
6949 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6950                                      struct LevelFileInfo *level_file_info,
6951                                      boolean level_info_only)
6952 {
6953   char *filename = level_file_info->filename;
6954   File *file;
6955   int num_magic_bytes = 8;
6956   char magic_bytes[num_magic_bytes + 1];
6957   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6958
6959   if (!(file = openFile(filename, MODE_READ)))
6960   {
6961     level->no_valid_file = TRUE;
6962
6963     if (!level_info_only)
6964       Warn("cannot read level '%s' -- using empty level", filename);
6965
6966     return;
6967   }
6968
6969   // fseek(file, 0x0000, SEEK_SET);
6970
6971   if (level_file_info->packed)
6972   {
6973     // read "magic bytes" from start of file
6974     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6975       magic_bytes[0] = '\0';
6976
6977     // check "magic bytes" for correct file format
6978     if (!strPrefix(magic_bytes, "DC2"))
6979     {
6980       level->no_valid_file = TRUE;
6981
6982       Warn("unknown DC level file '%s' -- using empty level", filename);
6983
6984       return;
6985     }
6986
6987     if (strPrefix(magic_bytes, "DC2Win95") ||
6988         strPrefix(magic_bytes, "DC2Win98"))
6989     {
6990       int position_first_level = 0x00fa;
6991       int extra_bytes = 4;
6992       int skip_bytes;
6993
6994       // advance file stream to first level inside the level package
6995       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6996
6997       // each block of level data is followed by block of non-level data
6998       num_levels_to_skip *= 2;
6999
7000       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
7001       while (num_levels_to_skip >= 0)
7002       {
7003         // advance file stream to next level inside the level package
7004         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7005         {
7006           level->no_valid_file = TRUE;
7007
7008           Warn("cannot fseek in file '%s' -- using empty level", filename);
7009
7010           return;
7011         }
7012
7013         // skip apparently unused extra bytes following each level
7014         ReadUnusedBytesFromFile(file, extra_bytes);
7015
7016         // read size of next level in level package
7017         skip_bytes = getFile32BitLE(file);
7018
7019         num_levels_to_skip--;
7020       }
7021     }
7022     else
7023     {
7024       level->no_valid_file = TRUE;
7025
7026       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7027
7028       return;
7029     }
7030   }
7031
7032   LoadLevelFromFileStream_DC(file, level);
7033
7034   closeFile(file);
7035 }
7036
7037
7038 // ----------------------------------------------------------------------------
7039 // functions for loading SB level
7040 // ----------------------------------------------------------------------------
7041
7042 int getMappedElement_SB(int element_ascii, boolean use_ces)
7043 {
7044   static struct
7045   {
7046     int ascii;
7047     int sb;
7048     int ce;
7049   }
7050   sb_element_mapping[] =
7051   {
7052     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7053     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7054     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7055     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7056     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7057     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7058     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7059     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7060
7061     { 0,   -1,                      -1          },
7062   };
7063
7064   int i;
7065
7066   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7067     if (element_ascii == sb_element_mapping[i].ascii)
7068       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7069
7070   return EL_UNDEFINED;
7071 }
7072
7073 static void SetLevelSettings_SB(struct LevelInfo *level)
7074 {
7075   // time settings
7076   level->time = 0;
7077   level->use_step_counter = TRUE;
7078
7079   // score settings
7080   level->score[SC_TIME_BONUS] = 0;
7081   level->time_score_base = 1;
7082   level->rate_time_over_score = TRUE;
7083
7084   // game settings
7085   level->auto_exit_sokoban = TRUE;
7086 }
7087
7088 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7089                                      struct LevelFileInfo *level_file_info,
7090                                      boolean level_info_only)
7091 {
7092   char *filename = level_file_info->filename;
7093   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7094   char last_comment[MAX_LINE_LEN];
7095   char level_name[MAX_LINE_LEN];
7096   char *line_ptr;
7097   File *file;
7098   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7099   boolean read_continued_line = FALSE;
7100   boolean reading_playfield = FALSE;
7101   boolean got_valid_playfield_line = FALSE;
7102   boolean invalid_playfield_char = FALSE;
7103   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7104   int file_level_nr = 0;
7105   int x = 0, y = 0;             // initialized to make compilers happy
7106
7107   last_comment[0] = '\0';
7108   level_name[0] = '\0';
7109
7110   if (!(file = openFile(filename, MODE_READ)))
7111   {
7112     level->no_valid_file = TRUE;
7113
7114     if (!level_info_only)
7115       Warn("cannot read level '%s' -- using empty level", filename);
7116
7117     return;
7118   }
7119
7120   while (!checkEndOfFile(file))
7121   {
7122     // level successfully read, but next level may follow here
7123     if (!got_valid_playfield_line && reading_playfield)
7124     {
7125       // read playfield from single level file -- skip remaining file
7126       if (!level_file_info->packed)
7127         break;
7128
7129       if (file_level_nr >= num_levels_to_skip)
7130         break;
7131
7132       file_level_nr++;
7133
7134       last_comment[0] = '\0';
7135       level_name[0] = '\0';
7136
7137       reading_playfield = FALSE;
7138     }
7139
7140     got_valid_playfield_line = FALSE;
7141
7142     // read next line of input file
7143     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7144       break;
7145
7146     // cut trailing line break (this can be newline and/or carriage return)
7147     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7148       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7149         *line_ptr = '\0';
7150
7151     // copy raw input line for later use (mainly debugging output)
7152     strcpy(line_raw, line);
7153
7154     if (read_continued_line)
7155     {
7156       // append new line to existing line, if there is enough space
7157       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7158         strcat(previous_line, line_ptr);
7159
7160       strcpy(line, previous_line);      // copy storage buffer to line
7161
7162       read_continued_line = FALSE;
7163     }
7164
7165     // if the last character is '\', continue at next line
7166     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7167     {
7168       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7169       strcpy(previous_line, line);      // copy line to storage buffer
7170
7171       read_continued_line = TRUE;
7172
7173       continue;
7174     }
7175
7176     // skip empty lines
7177     if (line[0] == '\0')
7178       continue;
7179
7180     // extract comment text from comment line
7181     if (line[0] == ';')
7182     {
7183       for (line_ptr = line; *line_ptr; line_ptr++)
7184         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7185           break;
7186
7187       strcpy(last_comment, line_ptr);
7188
7189       continue;
7190     }
7191
7192     // extract level title text from line containing level title
7193     if (line[0] == '\'')
7194     {
7195       strcpy(level_name, &line[1]);
7196
7197       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7198         level_name[strlen(level_name) - 1] = '\0';
7199
7200       continue;
7201     }
7202
7203     // skip lines containing only spaces (or empty lines)
7204     for (line_ptr = line; *line_ptr; line_ptr++)
7205       if (*line_ptr != ' ')
7206         break;
7207     if (*line_ptr == '\0')
7208       continue;
7209
7210     // at this point, we have found a line containing part of a playfield
7211
7212     got_valid_playfield_line = TRUE;
7213
7214     if (!reading_playfield)
7215     {
7216       reading_playfield = TRUE;
7217       invalid_playfield_char = FALSE;
7218
7219       for (x = 0; x < MAX_LEV_FIELDX; x++)
7220         for (y = 0; y < MAX_LEV_FIELDY; y++)
7221           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7222
7223       level->fieldx = 0;
7224       level->fieldy = 0;
7225
7226       // start with topmost tile row
7227       y = 0;
7228     }
7229
7230     // skip playfield line if larger row than allowed
7231     if (y >= MAX_LEV_FIELDY)
7232       continue;
7233
7234     // start with leftmost tile column
7235     x = 0;
7236
7237     // read playfield elements from line
7238     for (line_ptr = line; *line_ptr; line_ptr++)
7239     {
7240       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7241
7242       // stop parsing playfield line if larger column than allowed
7243       if (x >= MAX_LEV_FIELDX)
7244         break;
7245
7246       if (mapped_sb_element == EL_UNDEFINED)
7247       {
7248         invalid_playfield_char = TRUE;
7249
7250         break;
7251       }
7252
7253       level->field[x][y] = mapped_sb_element;
7254
7255       // continue with next tile column
7256       x++;
7257
7258       level->fieldx = MAX(x, level->fieldx);
7259     }
7260
7261     if (invalid_playfield_char)
7262     {
7263       // if first playfield line, treat invalid lines as comment lines
7264       if (y == 0)
7265         reading_playfield = FALSE;
7266
7267       continue;
7268     }
7269
7270     // continue with next tile row
7271     y++;
7272   }
7273
7274   closeFile(file);
7275
7276   level->fieldy = y;
7277
7278   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7279   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7280
7281   if (!reading_playfield)
7282   {
7283     level->no_valid_file = TRUE;
7284
7285     Warn("cannot read level '%s' -- using empty level", filename);
7286
7287     return;
7288   }
7289
7290   if (*level_name != '\0')
7291   {
7292     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7293     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7294   }
7295   else if (*last_comment != '\0')
7296   {
7297     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7298     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7299   }
7300   else
7301   {
7302     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7303   }
7304
7305   // set all empty fields beyond the border walls to invisible steel wall
7306   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7307   {
7308     if ((x == 0 || x == level->fieldx - 1 ||
7309          y == 0 || y == level->fieldy - 1) &&
7310         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7311       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7312                      level->field, level->fieldx, level->fieldy);
7313   }
7314
7315   // set special level settings for Sokoban levels
7316   SetLevelSettings_SB(level);
7317
7318   if (load_xsb_to_ces)
7319   {
7320     // special global settings can now be set in level template
7321     level->use_custom_template = TRUE;
7322   }
7323 }
7324
7325
7326 // -------------------------------------------------------------------------
7327 // functions for handling native levels
7328 // -------------------------------------------------------------------------
7329
7330 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7331                                      struct LevelFileInfo *level_file_info,
7332                                      boolean level_info_only)
7333 {
7334   int pos = 0;
7335
7336   // determine position of requested level inside level package
7337   if (level_file_info->packed)
7338     pos = level_file_info->nr - leveldir_current->first_level;
7339
7340   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7341     level->no_valid_file = TRUE;
7342 }
7343
7344 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7345                                      struct LevelFileInfo *level_file_info,
7346                                      boolean level_info_only)
7347 {
7348   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7349     level->no_valid_file = TRUE;
7350 }
7351
7352 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7353                                      struct LevelFileInfo *level_file_info,
7354                                      boolean level_info_only)
7355 {
7356   int pos = 0;
7357
7358   // determine position of requested level inside level package
7359   if (level_file_info->packed)
7360     pos = level_file_info->nr - leveldir_current->first_level;
7361
7362   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7363     level->no_valid_file = TRUE;
7364 }
7365
7366 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7367                                      struct LevelFileInfo *level_file_info,
7368                                      boolean level_info_only)
7369 {
7370   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7371     level->no_valid_file = TRUE;
7372 }
7373
7374 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7375 {
7376   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7377     CopyNativeLevel_RND_to_BD(level);
7378   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7379     CopyNativeLevel_RND_to_EM(level);
7380   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7381     CopyNativeLevel_RND_to_SP(level);
7382   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7383     CopyNativeLevel_RND_to_MM(level);
7384 }
7385
7386 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7387 {
7388   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7389     CopyNativeLevel_BD_to_RND(level);
7390   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7391     CopyNativeLevel_EM_to_RND(level);
7392   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7393     CopyNativeLevel_SP_to_RND(level);
7394   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7395     CopyNativeLevel_MM_to_RND(level);
7396 }
7397
7398 void SaveNativeLevel(struct LevelInfo *level)
7399 {
7400   // saving native level files only supported for some game engines
7401   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7402       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7403     return;
7404
7405   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7406                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7407   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7408   char *filename = getLevelFilenameFromBasename(basename);
7409
7410   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7411     return;
7412
7413   boolean success = FALSE;
7414
7415   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7416   {
7417     CopyNativeLevel_RND_to_BD(level);
7418     // CopyNativeTape_RND_to_BD(level);
7419
7420     success = SaveNativeLevel_BD(filename);
7421   }
7422   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7423   {
7424     CopyNativeLevel_RND_to_SP(level);
7425     CopyNativeTape_RND_to_SP(level);
7426
7427     success = SaveNativeLevel_SP(filename);
7428   }
7429
7430   if (success)
7431     Request("Native level file saved!", REQ_CONFIRM);
7432   else
7433     Request("Failed to save native level file!", REQ_CONFIRM);
7434 }
7435
7436
7437 // ----------------------------------------------------------------------------
7438 // functions for loading generic level
7439 // ----------------------------------------------------------------------------
7440
7441 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7442                                   struct LevelFileInfo *level_file_info,
7443                                   boolean level_info_only)
7444 {
7445   // always start with reliable default values
7446   setLevelInfoToDefaults(level, level_info_only, TRUE);
7447
7448   switch (level_file_info->type)
7449   {
7450     case LEVEL_FILE_TYPE_RND:
7451       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7452       break;
7453
7454     case LEVEL_FILE_TYPE_BD:
7455       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7456       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7457       break;
7458
7459     case LEVEL_FILE_TYPE_EM:
7460       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7461       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7462       break;
7463
7464     case LEVEL_FILE_TYPE_SP:
7465       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7466       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7467       break;
7468
7469     case LEVEL_FILE_TYPE_MM:
7470       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7471       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7472       break;
7473
7474     case LEVEL_FILE_TYPE_DC:
7475       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7476       break;
7477
7478     case LEVEL_FILE_TYPE_SB:
7479       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7480       break;
7481
7482     default:
7483       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7484       break;
7485   }
7486
7487   // if level file is invalid, restore level structure to default values
7488   if (level->no_valid_file)
7489     setLevelInfoToDefaults(level, level_info_only, FALSE);
7490
7491   if (check_special_flags("use_native_bd_game_engine"))
7492     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7493
7494   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7495     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7496
7497   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7498     CopyNativeLevel_Native_to_RND(level);
7499 }
7500
7501 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7502 {
7503   static struct LevelFileInfo level_file_info;
7504
7505   // always start with reliable default values
7506   setFileInfoToDefaults(&level_file_info);
7507
7508   level_file_info.nr = 0;                       // unknown level number
7509   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7510
7511   setString(&level_file_info.filename, filename);
7512
7513   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7514 }
7515
7516 static void LoadLevel_InitVersion(struct LevelInfo *level)
7517 {
7518   int i, j;
7519
7520   if (leveldir_current == NULL)         // only when dumping level
7521     return;
7522
7523   // all engine modifications also valid for levels which use latest engine
7524   if (level->game_version < VERSION_IDENT(3,2,0,5))
7525   {
7526     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7527     level->time_score_base = 10;
7528   }
7529
7530   if (leveldir_current->latest_engine)
7531   {
7532     // ---------- use latest game engine --------------------------------------
7533
7534     /* For all levels which are forced to use the latest game engine version
7535        (normally all but user contributed, private and undefined levels), set
7536        the game engine version to the actual version; this allows for actual
7537        corrections in the game engine to take effect for existing, converted
7538        levels (from "classic" or other existing games) to make the emulation
7539        of the corresponding game more accurate, while (hopefully) not breaking
7540        existing levels created from other players. */
7541
7542     level->game_version = GAME_VERSION_ACTUAL;
7543
7544     /* Set special EM style gems behaviour: EM style gems slip down from
7545        normal, steel and growing wall. As this is a more fundamental change,
7546        it seems better to set the default behaviour to "off" (as it is more
7547        natural) and make it configurable in the level editor (as a property
7548        of gem style elements). Already existing converted levels (neither
7549        private nor contributed levels) are changed to the new behaviour. */
7550
7551     if (level->file_version < FILE_VERSION_2_0)
7552       level->em_slippery_gems = TRUE;
7553
7554     return;
7555   }
7556
7557   // ---------- use game engine the level was created with --------------------
7558
7559   /* For all levels which are not forced to use the latest game engine
7560      version (normally user contributed, private and undefined levels),
7561      use the version of the game engine the levels were created for.
7562
7563      Since 2.0.1, the game engine version is now directly stored
7564      in the level file (chunk "VERS"), so there is no need anymore
7565      to set the game version from the file version (except for old,
7566      pre-2.0 levels, where the game version is still taken from the
7567      file format version used to store the level -- see above). */
7568
7569   // player was faster than enemies in 1.0.0 and before
7570   if (level->file_version == FILE_VERSION_1_0)
7571     for (i = 0; i < MAX_PLAYERS; i++)
7572       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7573
7574   // default behaviour for EM style gems was "slippery" only in 2.0.1
7575   if (level->game_version == VERSION_IDENT(2,0,1,0))
7576     level->em_slippery_gems = TRUE;
7577
7578   // springs could be pushed over pits before (pre-release version) 2.2.0
7579   if (level->game_version < VERSION_IDENT(2,2,0,0))
7580     level->use_spring_bug = TRUE;
7581
7582   if (level->game_version < VERSION_IDENT(3,2,0,5))
7583   {
7584     // time orb caused limited time in endless time levels before 3.2.0-5
7585     level->use_time_orb_bug = TRUE;
7586
7587     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7588     level->block_snap_field = FALSE;
7589
7590     // extra time score was same value as time left score before 3.2.0-5
7591     level->extra_time_score = level->score[SC_TIME_BONUS];
7592   }
7593
7594   if (level->game_version < VERSION_IDENT(3,2,0,7))
7595   {
7596     // default behaviour for snapping was "not continuous" before 3.2.0-7
7597     level->continuous_snapping = FALSE;
7598   }
7599
7600   // only few elements were able to actively move into acid before 3.1.0
7601   // trigger settings did not exist before 3.1.0; set to default "any"
7602   if (level->game_version < VERSION_IDENT(3,1,0,0))
7603   {
7604     // correct "can move into acid" settings (all zero in old levels)
7605
7606     level->can_move_into_acid_bits = 0; // nothing can move into acid
7607     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7608
7609     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7610     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7611     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7612     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7613
7614     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7615       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7616
7617     // correct trigger settings (stored as zero == "none" in old levels)
7618
7619     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7620     {
7621       int element = EL_CUSTOM_START + i;
7622       struct ElementInfo *ei = &element_info[element];
7623
7624       for (j = 0; j < ei->num_change_pages; j++)
7625       {
7626         struct ElementChangeInfo *change = &ei->change_page[j];
7627
7628         change->trigger_player = CH_PLAYER_ANY;
7629         change->trigger_page = CH_PAGE_ANY;
7630       }
7631     }
7632   }
7633
7634   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7635   {
7636     int element = EL_CUSTOM_256;
7637     struct ElementInfo *ei = &element_info[element];
7638     struct ElementChangeInfo *change = &ei->change_page[0];
7639
7640     /* This is needed to fix a problem that was caused by a bugfix in function
7641        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7642        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7643        not replace walkable elements, but instead just placed the player on it,
7644        without placing the Sokoban field under the player). Unfortunately, this
7645        breaks "Snake Bite" style levels when the snake is halfway through a door
7646        that just closes (the snake head is still alive and can be moved in this
7647        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7648        player (without Sokoban element) which then gets killed as designed). */
7649
7650     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7651          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7652         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7653       change->target_element = EL_PLAYER_1;
7654   }
7655
7656   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7657   if (level->game_version < VERSION_IDENT(3,2,5,0))
7658   {
7659     /* This is needed to fix a problem that was caused by a bugfix in function
7660        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7661        corrects the behaviour when a custom element changes to another custom
7662        element with a higher element number that has change actions defined.
7663        Normally, only one change per frame is allowed for custom elements.
7664        Therefore, it is checked if a custom element already changed in the
7665        current frame; if it did, subsequent changes are suppressed.
7666        Unfortunately, this is only checked for element changes, but not for
7667        change actions, which are still executed. As the function above loops
7668        through all custom elements from lower to higher, an element change
7669        resulting in a lower CE number won't be checked again, while a target
7670        element with a higher number will also be checked, and potential change
7671        actions will get executed for this CE, too (which is wrong), while
7672        further changes are ignored (which is correct). As this bugfix breaks
7673        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7674        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7675        behaviour for existing levels and tapes that make use of this bug */
7676
7677     level->use_action_after_change_bug = TRUE;
7678   }
7679
7680   // not centering level after relocating player was default only in 3.2.3
7681   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7682     level->shifted_relocation = TRUE;
7683
7684   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7685   if (level->game_version < VERSION_IDENT(3,2,6,0))
7686     level->em_explodes_by_fire = TRUE;
7687
7688   // levels were solved by the first player entering an exit up to 4.1.0.0
7689   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7690     level->solved_by_one_player = TRUE;
7691
7692   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7693   if (level->game_version < VERSION_IDENT(4,1,1,1))
7694     level->use_life_bugs = TRUE;
7695
7696   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7697   if (level->game_version < VERSION_IDENT(4,1,1,1))
7698     level->sb_objects_needed = FALSE;
7699
7700   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7701   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7702     level->finish_dig_collect = FALSE;
7703
7704   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7705   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7706     level->keep_walkable_ce = TRUE;
7707 }
7708
7709 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7710 {
7711   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7712   int x, y;
7713
7714   // check if this level is (not) a Sokoban level
7715   for (y = 0; y < level->fieldy; y++)
7716     for (x = 0; x < level->fieldx; x++)
7717       if (!IS_SB_ELEMENT(Tile[x][y]))
7718         is_sokoban_level = FALSE;
7719
7720   if (is_sokoban_level)
7721   {
7722     // set special level settings for Sokoban levels
7723     SetLevelSettings_SB(level);
7724   }
7725 }
7726
7727 static void LoadLevel_InitSettings(struct LevelInfo *level)
7728 {
7729   // adjust level settings for (non-native) Sokoban-style levels
7730   LoadLevel_InitSettings_SB(level);
7731
7732   // rename levels with title "nameless level" or if renaming is forced
7733   if (leveldir_current->empty_level_name != NULL &&
7734       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7735        leveldir_current->force_level_name))
7736     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7737              leveldir_current->empty_level_name, level_nr);
7738 }
7739
7740 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7741 {
7742   int i, x, y;
7743
7744   // map elements that have changed in newer versions
7745   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7746                                                     level->game_version);
7747   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7748     for (x = 0; x < 3; x++)
7749       for (y = 0; y < 3; y++)
7750         level->yamyam_content[i].e[x][y] =
7751           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7752                                     level->game_version);
7753
7754 }
7755
7756 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7757 {
7758   int i, j;
7759
7760   // map custom element change events that have changed in newer versions
7761   // (these following values were accidentally changed in version 3.0.1)
7762   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7763   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7764   {
7765     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7766     {
7767       int element = EL_CUSTOM_START + i;
7768
7769       // order of checking and copying events to be mapped is important
7770       // (do not change the start and end value -- they are constant)
7771       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7772       {
7773         if (HAS_CHANGE_EVENT(element, j - 2))
7774         {
7775           SET_CHANGE_EVENT(element, j - 2, FALSE);
7776           SET_CHANGE_EVENT(element, j, TRUE);
7777         }
7778       }
7779
7780       // order of checking and copying events to be mapped is important
7781       // (do not change the start and end value -- they are constant)
7782       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7783       {
7784         if (HAS_CHANGE_EVENT(element, j - 1))
7785         {
7786           SET_CHANGE_EVENT(element, j - 1, FALSE);
7787           SET_CHANGE_EVENT(element, j, TRUE);
7788         }
7789       }
7790     }
7791   }
7792
7793   // initialize "can_change" field for old levels with only one change page
7794   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7795   {
7796     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7797     {
7798       int element = EL_CUSTOM_START + i;
7799
7800       if (CAN_CHANGE(element))
7801         element_info[element].change->can_change = TRUE;
7802     }
7803   }
7804
7805   // correct custom element values (for old levels without these options)
7806   if (level->game_version < VERSION_IDENT(3,1,1,0))
7807   {
7808     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7809     {
7810       int element = EL_CUSTOM_START + i;
7811       struct ElementInfo *ei = &element_info[element];
7812
7813       if (ei->access_direction == MV_NO_DIRECTION)
7814         ei->access_direction = MV_ALL_DIRECTIONS;
7815     }
7816   }
7817
7818   // correct custom element values (fix invalid values for all versions)
7819   if (1)
7820   {
7821     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7822     {
7823       int element = EL_CUSTOM_START + i;
7824       struct ElementInfo *ei = &element_info[element];
7825
7826       for (j = 0; j < ei->num_change_pages; j++)
7827       {
7828         struct ElementChangeInfo *change = &ei->change_page[j];
7829
7830         if (change->trigger_player == CH_PLAYER_NONE)
7831           change->trigger_player = CH_PLAYER_ANY;
7832
7833         if (change->trigger_side == CH_SIDE_NONE)
7834           change->trigger_side = CH_SIDE_ANY;
7835       }
7836     }
7837   }
7838
7839   // initialize "can_explode" field for old levels which did not store this
7840   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7841   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7842   {
7843     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7844     {
7845       int element = EL_CUSTOM_START + i;
7846
7847       if (EXPLODES_1X1_OLD(element))
7848         element_info[element].explosion_type = EXPLODES_1X1;
7849
7850       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7851                                              EXPLODES_SMASHED(element) ||
7852                                              EXPLODES_IMPACT(element)));
7853     }
7854   }
7855
7856   // correct previously hard-coded move delay values for maze runner style
7857   if (level->game_version < VERSION_IDENT(3,1,1,0))
7858   {
7859     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7860     {
7861       int element = EL_CUSTOM_START + i;
7862
7863       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7864       {
7865         // previously hard-coded and therefore ignored
7866         element_info[element].move_delay_fixed = 9;
7867         element_info[element].move_delay_random = 0;
7868       }
7869     }
7870   }
7871
7872   // set some other uninitialized values of custom elements in older levels
7873   if (level->game_version < VERSION_IDENT(3,1,0,0))
7874   {
7875     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7876     {
7877       int element = EL_CUSTOM_START + i;
7878
7879       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7880
7881       element_info[element].explosion_delay = 17;
7882       element_info[element].ignition_delay = 8;
7883     }
7884   }
7885
7886   // set mouse click change events to work for left/middle/right mouse button
7887   if (level->game_version < VERSION_IDENT(4,2,3,0))
7888   {
7889     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7890     {
7891       int element = EL_CUSTOM_START + i;
7892       struct ElementInfo *ei = &element_info[element];
7893
7894       for (j = 0; j < ei->num_change_pages; j++)
7895       {
7896         struct ElementChangeInfo *change = &ei->change_page[j];
7897
7898         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7899             change->has_event[CE_PRESSED_BY_MOUSE] ||
7900             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7901             change->has_event[CE_MOUSE_PRESSED_ON_X])
7902           change->trigger_side = CH_SIDE_ANY;
7903       }
7904     }
7905   }
7906 }
7907
7908 static void LoadLevel_InitElements(struct LevelInfo *level)
7909 {
7910   LoadLevel_InitStandardElements(level);
7911
7912   if (level->file_has_custom_elements)
7913     LoadLevel_InitCustomElements(level);
7914
7915   // initialize element properties for level editor etc.
7916   InitElementPropertiesEngine(level->game_version);
7917   InitElementPropertiesGfxElement();
7918 }
7919
7920 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7921 {
7922   int x, y;
7923
7924   // map elements that have changed in newer versions
7925   for (y = 0; y < level->fieldy; y++)
7926     for (x = 0; x < level->fieldx; x++)
7927       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7928                                                      level->game_version);
7929
7930   // clear unused playfield data (nicer if level gets resized in editor)
7931   for (x = 0; x < MAX_LEV_FIELDX; x++)
7932     for (y = 0; y < MAX_LEV_FIELDY; y++)
7933       if (x >= level->fieldx || y >= level->fieldy)
7934         level->field[x][y] = EL_EMPTY;
7935
7936   // copy elements to runtime playfield array
7937   for (x = 0; x < MAX_LEV_FIELDX; x++)
7938     for (y = 0; y < MAX_LEV_FIELDY; y++)
7939       Tile[x][y] = level->field[x][y];
7940
7941   // initialize level size variables for faster access
7942   lev_fieldx = level->fieldx;
7943   lev_fieldy = level->fieldy;
7944
7945   // determine border element for this level
7946   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7947     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7948   else
7949     SetBorderElement();
7950 }
7951
7952 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7953 {
7954   struct LevelFileInfo *level_file_info = &level->file_info;
7955
7956   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7957     CopyNativeLevel_RND_to_Native(level);
7958 }
7959
7960 static void LoadLevelTemplate_LoadAndInit(void)
7961 {
7962   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7963
7964   LoadLevel_InitVersion(&level_template);
7965   LoadLevel_InitElements(&level_template);
7966   LoadLevel_InitSettings(&level_template);
7967
7968   ActivateLevelTemplate();
7969 }
7970
7971 void LoadLevelTemplate(int nr)
7972 {
7973   if (!fileExists(getGlobalLevelTemplateFilename()))
7974   {
7975     Warn("no level template found for this level");
7976
7977     return;
7978   }
7979
7980   setLevelFileInfo(&level_template.file_info, nr);
7981
7982   LoadLevelTemplate_LoadAndInit();
7983 }
7984
7985 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7986 {
7987   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7988
7989   LoadLevelTemplate_LoadAndInit();
7990 }
7991
7992 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7993 {
7994   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7995
7996   if (level.use_custom_template)
7997   {
7998     if (network_level != NULL)
7999       LoadNetworkLevelTemplate(network_level);
8000     else
8001       LoadLevelTemplate(-1);
8002   }
8003
8004   LoadLevel_InitVersion(&level);
8005   LoadLevel_InitElements(&level);
8006   LoadLevel_InitPlayfield(&level);
8007   LoadLevel_InitSettings(&level);
8008
8009   LoadLevel_InitNativeEngines(&level);
8010 }
8011
8012 void LoadLevel(int nr)
8013 {
8014   SetLevelSetInfo(leveldir_current->identifier, nr);
8015
8016   setLevelFileInfo(&level.file_info, nr);
8017
8018   LoadLevel_LoadAndInit(NULL);
8019 }
8020
8021 void LoadLevelInfoOnly(int nr)
8022 {
8023   setLevelFileInfo(&level.file_info, nr);
8024
8025   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8026 }
8027
8028 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8029 {
8030   SetLevelSetInfo(network_level->leveldir_identifier,
8031                   network_level->file_info.nr);
8032
8033   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8034
8035   LoadLevel_LoadAndInit(network_level);
8036 }
8037
8038 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8039 {
8040   int chunk_size = 0;
8041
8042   chunk_size += putFileVersion(file, level->file_version);
8043   chunk_size += putFileVersion(file, level->game_version);
8044
8045   return chunk_size;
8046 }
8047
8048 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8049 {
8050   int chunk_size = 0;
8051
8052   chunk_size += putFile16BitBE(file, level->creation_date.year);
8053   chunk_size += putFile8Bit(file,    level->creation_date.month);
8054   chunk_size += putFile8Bit(file,    level->creation_date.day);
8055
8056   return chunk_size;
8057 }
8058
8059 #if ENABLE_HISTORIC_CHUNKS
8060 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8061 {
8062   int i, x, y;
8063
8064   putFile8Bit(file, level->fieldx);
8065   putFile8Bit(file, level->fieldy);
8066
8067   putFile16BitBE(file, level->time);
8068   putFile16BitBE(file, level->gems_needed);
8069
8070   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8071     putFile8Bit(file, level->name[i]);
8072
8073   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8074     putFile8Bit(file, level->score[i]);
8075
8076   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8077     for (y = 0; y < 3; y++)
8078       for (x = 0; x < 3; x++)
8079         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8080                            level->yamyam_content[i].e[x][y]));
8081   putFile8Bit(file, level->amoeba_speed);
8082   putFile8Bit(file, level->time_magic_wall);
8083   putFile8Bit(file, level->time_wheel);
8084   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8085                      level->amoeba_content));
8086   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8087   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8088   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8089   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8090
8091   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8092
8093   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8094   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8095   putFile32BitBE(file, level->can_move_into_acid_bits);
8096   putFile8Bit(file, level->dont_collide_with_bits);
8097
8098   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8099   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8100
8101   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8102   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8103   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8104
8105   putFile8Bit(file, level->game_engine_type);
8106
8107   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8108 }
8109 #endif
8110
8111 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8112 {
8113   int chunk_size = 0;
8114   int i;
8115
8116   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8117     chunk_size += putFile8Bit(file, level->name[i]);
8118
8119   return chunk_size;
8120 }
8121
8122 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8123 {
8124   int chunk_size = 0;
8125   int i;
8126
8127   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8128     chunk_size += putFile8Bit(file, level->author[i]);
8129
8130   return chunk_size;
8131 }
8132
8133 #if ENABLE_HISTORIC_CHUNKS
8134 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8135 {
8136   int chunk_size = 0;
8137   int x, y;
8138
8139   for (y = 0; y < level->fieldy; y++)
8140     for (x = 0; x < level->fieldx; x++)
8141       if (level->encoding_16bit_field)
8142         chunk_size += putFile16BitBE(file, level->field[x][y]);
8143       else
8144         chunk_size += putFile8Bit(file, level->field[x][y]);
8145
8146   return chunk_size;
8147 }
8148 #endif
8149
8150 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8151 {
8152   int chunk_size = 0;
8153   int x, y;
8154
8155   for (y = 0; y < level->fieldy; y++) 
8156     for (x = 0; x < level->fieldx; x++) 
8157       chunk_size += putFile16BitBE(file, level->field[x][y]);
8158
8159   return chunk_size;
8160 }
8161
8162 #if ENABLE_HISTORIC_CHUNKS
8163 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8164 {
8165   int i, x, y;
8166
8167   putFile8Bit(file, EL_YAMYAM);
8168   putFile8Bit(file, level->num_yamyam_contents);
8169   putFile8Bit(file, 0);
8170   putFile8Bit(file, 0);
8171
8172   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8173     for (y = 0; y < 3; y++)
8174       for (x = 0; x < 3; x++)
8175         if (level->encoding_16bit_field)
8176           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8177         else
8178           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8179 }
8180 #endif
8181
8182 #if ENABLE_HISTORIC_CHUNKS
8183 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8184 {
8185   int i, x, y;
8186   int num_contents, content_xsize, content_ysize;
8187   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8188
8189   if (element == EL_YAMYAM)
8190   {
8191     num_contents = level->num_yamyam_contents;
8192     content_xsize = 3;
8193     content_ysize = 3;
8194
8195     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8196       for (y = 0; y < 3; y++)
8197         for (x = 0; x < 3; x++)
8198           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8199   }
8200   else if (element == EL_BD_AMOEBA)
8201   {
8202     num_contents = 1;
8203     content_xsize = 1;
8204     content_ysize = 1;
8205
8206     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8207       for (y = 0; y < 3; y++)
8208         for (x = 0; x < 3; x++)
8209           content_array[i][x][y] = EL_EMPTY;
8210     content_array[0][0][0] = level->amoeba_content;
8211   }
8212   else
8213   {
8214     // chunk header already written -- write empty chunk data
8215     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8216
8217     Warn("cannot save content for element '%d'", element);
8218
8219     return;
8220   }
8221
8222   putFile16BitBE(file, element);
8223   putFile8Bit(file, num_contents);
8224   putFile8Bit(file, content_xsize);
8225   putFile8Bit(file, content_ysize);
8226
8227   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8228
8229   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8230     for (y = 0; y < 3; y++)
8231       for (x = 0; x < 3; x++)
8232         putFile16BitBE(file, content_array[i][x][y]);
8233 }
8234 #endif
8235
8236 #if ENABLE_HISTORIC_CHUNKS
8237 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8238 {
8239   int envelope_nr = element - EL_ENVELOPE_1;
8240   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8241   int chunk_size = 0;
8242   int i;
8243
8244   chunk_size += putFile16BitBE(file, element);
8245   chunk_size += putFile16BitBE(file, envelope_len);
8246   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8247   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8248
8249   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8250   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8251
8252   for (i = 0; i < envelope_len; i++)
8253     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8254
8255   return chunk_size;
8256 }
8257 #endif
8258
8259 #if ENABLE_HISTORIC_CHUNKS
8260 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8261                            int num_changed_custom_elements)
8262 {
8263   int i, check = 0;
8264
8265   putFile16BitBE(file, num_changed_custom_elements);
8266
8267   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8268   {
8269     int element = EL_CUSTOM_START + i;
8270
8271     struct ElementInfo *ei = &element_info[element];
8272
8273     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8274     {
8275       if (check < num_changed_custom_elements)
8276       {
8277         putFile16BitBE(file, element);
8278         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8279       }
8280
8281       check++;
8282     }
8283   }
8284
8285   if (check != num_changed_custom_elements)     // should not happen
8286     Warn("inconsistent number of custom element properties");
8287 }
8288 #endif
8289
8290 #if ENABLE_HISTORIC_CHUNKS
8291 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8292                            int num_changed_custom_elements)
8293 {
8294   int i, check = 0;
8295
8296   putFile16BitBE(file, num_changed_custom_elements);
8297
8298   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8299   {
8300     int element = EL_CUSTOM_START + i;
8301
8302     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8303     {
8304       if (check < num_changed_custom_elements)
8305       {
8306         putFile16BitBE(file, element);
8307         putFile16BitBE(file, element_info[element].change->target_element);
8308       }
8309
8310       check++;
8311     }
8312   }
8313
8314   if (check != num_changed_custom_elements)     // should not happen
8315     Warn("inconsistent number of custom target elements");
8316 }
8317 #endif
8318
8319 #if ENABLE_HISTORIC_CHUNKS
8320 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8321                            int num_changed_custom_elements)
8322 {
8323   int i, j, x, y, check = 0;
8324
8325   putFile16BitBE(file, num_changed_custom_elements);
8326
8327   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8328   {
8329     int element = EL_CUSTOM_START + i;
8330     struct ElementInfo *ei = &element_info[element];
8331
8332     if (ei->modified_settings)
8333     {
8334       if (check < num_changed_custom_elements)
8335       {
8336         putFile16BitBE(file, element);
8337
8338         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8339           putFile8Bit(file, ei->description[j]);
8340
8341         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8342
8343         // some free bytes for future properties and padding
8344         WriteUnusedBytesToFile(file, 7);
8345
8346         putFile8Bit(file, ei->use_gfx_element);
8347         putFile16BitBE(file, ei->gfx_element_initial);
8348
8349         putFile8Bit(file, ei->collect_score_initial);
8350         putFile8Bit(file, ei->collect_count_initial);
8351
8352         putFile16BitBE(file, ei->push_delay_fixed);
8353         putFile16BitBE(file, ei->push_delay_random);
8354         putFile16BitBE(file, ei->move_delay_fixed);
8355         putFile16BitBE(file, ei->move_delay_random);
8356
8357         putFile16BitBE(file, ei->move_pattern);
8358         putFile8Bit(file, ei->move_direction_initial);
8359         putFile8Bit(file, ei->move_stepsize);
8360
8361         for (y = 0; y < 3; y++)
8362           for (x = 0; x < 3; x++)
8363             putFile16BitBE(file, ei->content.e[x][y]);
8364
8365         putFile32BitBE(file, ei->change->events);
8366
8367         putFile16BitBE(file, ei->change->target_element);
8368
8369         putFile16BitBE(file, ei->change->delay_fixed);
8370         putFile16BitBE(file, ei->change->delay_random);
8371         putFile16BitBE(file, ei->change->delay_frames);
8372
8373         putFile16BitBE(file, ei->change->initial_trigger_element);
8374
8375         putFile8Bit(file, ei->change->explode);
8376         putFile8Bit(file, ei->change->use_target_content);
8377         putFile8Bit(file, ei->change->only_if_complete);
8378         putFile8Bit(file, ei->change->use_random_replace);
8379
8380         putFile8Bit(file, ei->change->random_percentage);
8381         putFile8Bit(file, ei->change->replace_when);
8382
8383         for (y = 0; y < 3; y++)
8384           for (x = 0; x < 3; x++)
8385             putFile16BitBE(file, ei->change->content.e[x][y]);
8386
8387         putFile8Bit(file, ei->slippery_type);
8388
8389         // some free bytes for future properties and padding
8390         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8391       }
8392
8393       check++;
8394     }
8395   }
8396
8397   if (check != num_changed_custom_elements)     // should not happen
8398     Warn("inconsistent number of custom element properties");
8399 }
8400 #endif
8401
8402 #if ENABLE_HISTORIC_CHUNKS
8403 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8404 {
8405   struct ElementInfo *ei = &element_info[element];
8406   int i, j, x, y;
8407
8408   // ---------- custom element base property values (96 bytes) ----------------
8409
8410   putFile16BitBE(file, element);
8411
8412   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8413     putFile8Bit(file, ei->description[i]);
8414
8415   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8416
8417   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8418
8419   putFile8Bit(file, ei->num_change_pages);
8420
8421   putFile16BitBE(file, ei->ce_value_fixed_initial);
8422   putFile16BitBE(file, ei->ce_value_random_initial);
8423   putFile8Bit(file, ei->use_last_ce_value);
8424
8425   putFile8Bit(file, ei->use_gfx_element);
8426   putFile16BitBE(file, ei->gfx_element_initial);
8427
8428   putFile8Bit(file, ei->collect_score_initial);
8429   putFile8Bit(file, ei->collect_count_initial);
8430
8431   putFile8Bit(file, ei->drop_delay_fixed);
8432   putFile8Bit(file, ei->push_delay_fixed);
8433   putFile8Bit(file, ei->drop_delay_random);
8434   putFile8Bit(file, ei->push_delay_random);
8435   putFile16BitBE(file, ei->move_delay_fixed);
8436   putFile16BitBE(file, ei->move_delay_random);
8437
8438   // bits 0 - 15 of "move_pattern" ...
8439   putFile16BitBE(file, ei->move_pattern & 0xffff);
8440   putFile8Bit(file, ei->move_direction_initial);
8441   putFile8Bit(file, ei->move_stepsize);
8442
8443   putFile8Bit(file, ei->slippery_type);
8444
8445   for (y = 0; y < 3; y++)
8446     for (x = 0; x < 3; x++)
8447       putFile16BitBE(file, ei->content.e[x][y]);
8448
8449   putFile16BitBE(file, ei->move_enter_element);
8450   putFile16BitBE(file, ei->move_leave_element);
8451   putFile8Bit(file, ei->move_leave_type);
8452
8453   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8454   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8455
8456   putFile8Bit(file, ei->access_direction);
8457
8458   putFile8Bit(file, ei->explosion_delay);
8459   putFile8Bit(file, ei->ignition_delay);
8460   putFile8Bit(file, ei->explosion_type);
8461
8462   // some free bytes for future custom property values and padding
8463   WriteUnusedBytesToFile(file, 1);
8464
8465   // ---------- change page property values (48 bytes) ------------------------
8466
8467   for (i = 0; i < ei->num_change_pages; i++)
8468   {
8469     struct ElementChangeInfo *change = &ei->change_page[i];
8470     unsigned int event_bits;
8471
8472     // bits 0 - 31 of "has_event[]" ...
8473     event_bits = 0;
8474     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8475       if (change->has_event[j])
8476         event_bits |= (1u << j);
8477     putFile32BitBE(file, event_bits);
8478
8479     putFile16BitBE(file, change->target_element);
8480
8481     putFile16BitBE(file, change->delay_fixed);
8482     putFile16BitBE(file, change->delay_random);
8483     putFile16BitBE(file, change->delay_frames);
8484
8485     putFile16BitBE(file, change->initial_trigger_element);
8486
8487     putFile8Bit(file, change->explode);
8488     putFile8Bit(file, change->use_target_content);
8489     putFile8Bit(file, change->only_if_complete);
8490     putFile8Bit(file, change->use_random_replace);
8491
8492     putFile8Bit(file, change->random_percentage);
8493     putFile8Bit(file, change->replace_when);
8494
8495     for (y = 0; y < 3; y++)
8496       for (x = 0; x < 3; x++)
8497         putFile16BitBE(file, change->target_content.e[x][y]);
8498
8499     putFile8Bit(file, change->can_change);
8500
8501     putFile8Bit(file, change->trigger_side);
8502
8503     putFile8Bit(file, change->trigger_player);
8504     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8505                        log_2(change->trigger_page)));
8506
8507     putFile8Bit(file, change->has_action);
8508     putFile8Bit(file, change->action_type);
8509     putFile8Bit(file, change->action_mode);
8510     putFile16BitBE(file, change->action_arg);
8511
8512     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8513     event_bits = 0;
8514     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8515       if (change->has_event[j])
8516         event_bits |= (1u << (j - 32));
8517     putFile8Bit(file, event_bits);
8518   }
8519 }
8520 #endif
8521
8522 #if ENABLE_HISTORIC_CHUNKS
8523 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8524 {
8525   struct ElementInfo *ei = &element_info[element];
8526   struct ElementGroupInfo *group = ei->group;
8527   int i;
8528
8529   putFile16BitBE(file, element);
8530
8531   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8532     putFile8Bit(file, ei->description[i]);
8533
8534   putFile8Bit(file, group->num_elements);
8535
8536   putFile8Bit(file, ei->use_gfx_element);
8537   putFile16BitBE(file, ei->gfx_element_initial);
8538
8539   putFile8Bit(file, group->choice_mode);
8540
8541   // some free bytes for future values and padding
8542   WriteUnusedBytesToFile(file, 3);
8543
8544   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8545     putFile16BitBE(file, group->element[i]);
8546 }
8547 #endif
8548
8549 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8550                                 boolean write_element)
8551 {
8552   int save_type = entry->save_type;
8553   int data_type = entry->data_type;
8554   int conf_type = entry->conf_type;
8555   int byte_mask = conf_type & CONF_MASK_BYTES;
8556   int element = entry->element;
8557   int default_value = entry->default_value;
8558   int num_bytes = 0;
8559   boolean modified = FALSE;
8560
8561   if (byte_mask != CONF_MASK_MULTI_BYTES)
8562   {
8563     void *value_ptr = entry->value;
8564     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8565                  *(int *)value_ptr);
8566
8567     // check if any settings have been modified before saving them
8568     if (value != default_value)
8569       modified = TRUE;
8570
8571     // do not save if explicitly told or if unmodified default settings
8572     if ((save_type == SAVE_CONF_NEVER) ||
8573         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8574       return 0;
8575
8576     if (write_element)
8577       num_bytes += putFile16BitBE(file, element);
8578
8579     num_bytes += putFile8Bit(file, conf_type);
8580     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8581                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8582                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8583                   0);
8584   }
8585   else if (data_type == TYPE_STRING)
8586   {
8587     char *default_string = entry->default_string;
8588     char *string = (char *)(entry->value);
8589     int string_length = strlen(string);
8590     int i;
8591
8592     // check if any settings have been modified before saving them
8593     if (!strEqual(string, default_string))
8594       modified = TRUE;
8595
8596     // do not save if explicitly told or if unmodified default settings
8597     if ((save_type == SAVE_CONF_NEVER) ||
8598         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8599       return 0;
8600
8601     if (write_element)
8602       num_bytes += putFile16BitBE(file, element);
8603
8604     num_bytes += putFile8Bit(file, conf_type);
8605     num_bytes += putFile16BitBE(file, string_length);
8606
8607     for (i = 0; i < string_length; i++)
8608       num_bytes += putFile8Bit(file, string[i]);
8609   }
8610   else if (data_type == TYPE_ELEMENT_LIST)
8611   {
8612     int *element_array = (int *)(entry->value);
8613     int num_elements = *(int *)(entry->num_entities);
8614     int i;
8615
8616     // check if any settings have been modified before saving them
8617     for (i = 0; i < num_elements; i++)
8618       if (element_array[i] != default_value)
8619         modified = TRUE;
8620
8621     // do not save if explicitly told or if unmodified default settings
8622     if ((save_type == SAVE_CONF_NEVER) ||
8623         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8624       return 0;
8625
8626     if (write_element)
8627       num_bytes += putFile16BitBE(file, element);
8628
8629     num_bytes += putFile8Bit(file, conf_type);
8630     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8631
8632     for (i = 0; i < num_elements; i++)
8633       num_bytes += putFile16BitBE(file, element_array[i]);
8634   }
8635   else if (data_type == TYPE_CONTENT_LIST)
8636   {
8637     struct Content *content = (struct Content *)(entry->value);
8638     int num_contents = *(int *)(entry->num_entities);
8639     int i, x, y;
8640
8641     // check if any settings have been modified before saving them
8642     for (i = 0; i < num_contents; i++)
8643       for (y = 0; y < 3; y++)
8644         for (x = 0; x < 3; x++)
8645           if (content[i].e[x][y] != default_value)
8646             modified = TRUE;
8647
8648     // do not save if explicitly told or if unmodified default settings
8649     if ((save_type == SAVE_CONF_NEVER) ||
8650         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8651       return 0;
8652
8653     if (write_element)
8654       num_bytes += putFile16BitBE(file, element);
8655
8656     num_bytes += putFile8Bit(file, conf_type);
8657     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8658
8659     for (i = 0; i < num_contents; i++)
8660       for (y = 0; y < 3; y++)
8661         for (x = 0; x < 3; x++)
8662           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8663   }
8664
8665   return num_bytes;
8666 }
8667
8668 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8669 {
8670   int chunk_size = 0;
8671   int i;
8672
8673   li = *level;          // copy level data into temporary buffer
8674
8675   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8676     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8677
8678   return chunk_size;
8679 }
8680
8681 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8682 {
8683   int chunk_size = 0;
8684   int i;
8685
8686   li = *level;          // copy level data into temporary buffer
8687
8688   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8689     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8690
8691   return chunk_size;
8692 }
8693
8694 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8695 {
8696   int envelope_nr = element - EL_ENVELOPE_1;
8697   int chunk_size = 0;
8698   int i;
8699
8700   chunk_size += putFile16BitBE(file, element);
8701
8702   // copy envelope data into temporary buffer
8703   xx_envelope = level->envelope[envelope_nr];
8704
8705   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8706     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8707
8708   return chunk_size;
8709 }
8710
8711 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8712 {
8713   struct ElementInfo *ei = &element_info[element];
8714   int chunk_size = 0;
8715   int i, j;
8716
8717   chunk_size += putFile16BitBE(file, element);
8718
8719   xx_ei = *ei;          // copy element data into temporary buffer
8720
8721   // set default description string for this specific element
8722   strcpy(xx_default_description, getDefaultElementDescription(ei));
8723
8724   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8725     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8726
8727   for (i = 0; i < ei->num_change_pages; i++)
8728   {
8729     struct ElementChangeInfo *change = &ei->change_page[i];
8730
8731     xx_current_change_page = i;
8732
8733     xx_change = *change;        // copy change data into temporary buffer
8734
8735     resetEventBits();
8736     setEventBitsFromEventFlags(change);
8737
8738     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8739       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8740                                          FALSE);
8741   }
8742
8743   return chunk_size;
8744 }
8745
8746 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8747 {
8748   struct ElementInfo *ei = &element_info[element];
8749   struct ElementGroupInfo *group = ei->group;
8750   int chunk_size = 0;
8751   int i;
8752
8753   chunk_size += putFile16BitBE(file, element);
8754
8755   xx_ei = *ei;          // copy element data into temporary buffer
8756   xx_group = *group;    // copy group data into temporary buffer
8757
8758   // set default description string for this specific element
8759   strcpy(xx_default_description, getDefaultElementDescription(ei));
8760
8761   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8762     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8763
8764   return chunk_size;
8765 }
8766
8767 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8768 {
8769   struct ElementInfo *ei = &element_info[element];
8770   int chunk_size = 0;
8771   int i;
8772
8773   chunk_size += putFile16BitBE(file, element);
8774
8775   xx_ei = *ei;          // copy element data into temporary buffer
8776
8777   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8778     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8779
8780   return chunk_size;
8781 }
8782
8783 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8784                                   boolean save_as_template)
8785 {
8786   int chunk_size;
8787   int i;
8788   FILE *file;
8789
8790   if (!(file = fopen(filename, MODE_WRITE)))
8791   {
8792     Warn("cannot save level file '%s'", filename);
8793
8794     return;
8795   }
8796
8797   level->file_version = FILE_VERSION_ACTUAL;
8798   level->game_version = GAME_VERSION_ACTUAL;
8799
8800   level->creation_date = getCurrentDate();
8801
8802   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8803   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8804
8805   chunk_size = SaveLevel_VERS(NULL, level);
8806   putFileChunkBE(file, "VERS", chunk_size);
8807   SaveLevel_VERS(file, level);
8808
8809   chunk_size = SaveLevel_DATE(NULL, level);
8810   putFileChunkBE(file, "DATE", chunk_size);
8811   SaveLevel_DATE(file, level);
8812
8813   chunk_size = SaveLevel_NAME(NULL, level);
8814   putFileChunkBE(file, "NAME", chunk_size);
8815   SaveLevel_NAME(file, level);
8816
8817   chunk_size = SaveLevel_AUTH(NULL, level);
8818   putFileChunkBE(file, "AUTH", chunk_size);
8819   SaveLevel_AUTH(file, level);
8820
8821   chunk_size = SaveLevel_INFO(NULL, level);
8822   putFileChunkBE(file, "INFO", chunk_size);
8823   SaveLevel_INFO(file, level);
8824
8825   chunk_size = SaveLevel_BODY(NULL, level);
8826   putFileChunkBE(file, "BODY", chunk_size);
8827   SaveLevel_BODY(file, level);
8828
8829   chunk_size = SaveLevel_ELEM(NULL, level);
8830   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8831   {
8832     putFileChunkBE(file, "ELEM", chunk_size);
8833     SaveLevel_ELEM(file, level);
8834   }
8835
8836   for (i = 0; i < NUM_ENVELOPES; i++)
8837   {
8838     int element = EL_ENVELOPE_1 + i;
8839
8840     chunk_size = SaveLevel_NOTE(NULL, level, element);
8841     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8842     {
8843       putFileChunkBE(file, "NOTE", chunk_size);
8844       SaveLevel_NOTE(file, level, element);
8845     }
8846   }
8847
8848   // if not using template level, check for non-default custom/group elements
8849   if (!level->use_custom_template || save_as_template)
8850   {
8851     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8852     {
8853       int element = EL_CUSTOM_START + i;
8854
8855       chunk_size = SaveLevel_CUSX(NULL, level, element);
8856       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8857       {
8858         putFileChunkBE(file, "CUSX", chunk_size);
8859         SaveLevel_CUSX(file, level, element);
8860       }
8861     }
8862
8863     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8864     {
8865       int element = EL_GROUP_START + i;
8866
8867       chunk_size = SaveLevel_GRPX(NULL, level, element);
8868       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8869       {
8870         putFileChunkBE(file, "GRPX", chunk_size);
8871         SaveLevel_GRPX(file, level, element);
8872       }
8873     }
8874
8875     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8876     {
8877       int element = GET_EMPTY_ELEMENT(i);
8878
8879       chunk_size = SaveLevel_EMPX(NULL, level, element);
8880       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8881       {
8882         putFileChunkBE(file, "EMPX", chunk_size);
8883         SaveLevel_EMPX(file, level, element);
8884       }
8885     }
8886   }
8887
8888   fclose(file);
8889
8890   SetFilePermissions(filename, PERMS_PRIVATE);
8891 }
8892
8893 void SaveLevel(int nr)
8894 {
8895   char *filename = getDefaultLevelFilename(nr);
8896
8897   SaveLevelFromFilename(&level, filename, FALSE);
8898 }
8899
8900 void SaveLevelTemplate(void)
8901 {
8902   char *filename = getLocalLevelTemplateFilename();
8903
8904   SaveLevelFromFilename(&level, filename, TRUE);
8905 }
8906
8907 boolean SaveLevelChecked(int nr)
8908 {
8909   char *filename = getDefaultLevelFilename(nr);
8910   boolean new_level = !fileExists(filename);
8911   boolean level_saved = FALSE;
8912
8913   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8914   {
8915     SaveLevel(nr);
8916
8917     if (new_level)
8918       Request("Level saved!", REQ_CONFIRM);
8919
8920     level_saved = TRUE;
8921   }
8922
8923   return level_saved;
8924 }
8925
8926 void DumpLevel(struct LevelInfo *level)
8927 {
8928   if (level->no_level_file || level->no_valid_file)
8929   {
8930     Warn("cannot dump -- no valid level file found");
8931
8932     return;
8933   }
8934
8935   PrintLine("-", 79);
8936   Print("Level xxx (file version %08d, game version %08d)\n",
8937         level->file_version, level->game_version);
8938   PrintLine("-", 79);
8939
8940   Print("Level author: '%s'\n", level->author);
8941   Print("Level title:  '%s'\n", level->name);
8942   Print("\n");
8943   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8944   Print("\n");
8945   Print("Level time:  %d seconds\n", level->time);
8946   Print("Gems needed: %d\n", level->gems_needed);
8947   Print("\n");
8948   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8949   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8950   Print("Time for light:      %d seconds\n", level->time_light);
8951   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8952   Print("\n");
8953   Print("Amoeba speed: %d\n", level->amoeba_speed);
8954   Print("\n");
8955
8956   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8957   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8958   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8959   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8960   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8961   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8962
8963   if (options.debug)
8964   {
8965     int i, j;
8966
8967     for (i = 0; i < NUM_ENVELOPES; i++)
8968     {
8969       char *text = level->envelope[i].text;
8970       int text_len = strlen(text);
8971       boolean has_text = FALSE;
8972
8973       for (j = 0; j < text_len; j++)
8974         if (text[j] != ' ' && text[j] != '\n')
8975           has_text = TRUE;
8976
8977       if (has_text)
8978       {
8979         Print("\n");
8980         Print("Envelope %d:\n'%s'\n", i + 1, text);
8981       }
8982     }
8983   }
8984
8985   PrintLine("-", 79);
8986 }
8987
8988 void DumpLevels(void)
8989 {
8990   static LevelDirTree *dumplevel_leveldir = NULL;
8991
8992   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8993                                                  global.dumplevel_leveldir);
8994
8995   if (dumplevel_leveldir == NULL)
8996     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8997
8998   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8999       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
9000     Fail("no such level number: %d", global.dumplevel_level_nr);
9001
9002   leveldir_current = dumplevel_leveldir;
9003
9004   LoadLevel(global.dumplevel_level_nr);
9005   DumpLevel(&level);
9006
9007   CloseAllAndExit(0);
9008 }
9009
9010
9011 // ============================================================================
9012 // tape file functions
9013 // ============================================================================
9014
9015 static void setTapeInfoToDefaults(void)
9016 {
9017   int i;
9018
9019   // always start with reliable default values (empty tape)
9020   TapeErase();
9021
9022   // default values (also for pre-1.2 tapes) with only the first player
9023   tape.player_participates[0] = TRUE;
9024   for (i = 1; i < MAX_PLAYERS; i++)
9025     tape.player_participates[i] = FALSE;
9026
9027   // at least one (default: the first) player participates in every tape
9028   tape.num_participating_players = 1;
9029
9030   tape.property_bits = TAPE_PROPERTY_NONE;
9031
9032   tape.level_nr = level_nr;
9033   tape.counter = 0;
9034   tape.changed = FALSE;
9035   tape.solved = FALSE;
9036
9037   tape.recording = FALSE;
9038   tape.playing = FALSE;
9039   tape.pausing = FALSE;
9040
9041   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9042   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9043
9044   tape.no_info_chunk = TRUE;
9045   tape.no_valid_file = FALSE;
9046 }
9047
9048 static int getTapePosSize(struct TapeInfo *tape)
9049 {
9050   int tape_pos_size = 0;
9051
9052   if (tape->use_key_actions)
9053     tape_pos_size += tape->num_participating_players;
9054
9055   if (tape->use_mouse_actions)
9056     tape_pos_size += 3;         // x and y position and mouse button mask
9057
9058   tape_pos_size += 1;           // tape action delay value
9059
9060   return tape_pos_size;
9061 }
9062
9063 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9064 {
9065   tape->use_key_actions = FALSE;
9066   tape->use_mouse_actions = FALSE;
9067
9068   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9069     tape->use_key_actions = TRUE;
9070
9071   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9072     tape->use_mouse_actions = TRUE;
9073 }
9074
9075 static int getTapeActionValue(struct TapeInfo *tape)
9076 {
9077   return (tape->use_key_actions &&
9078           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9079           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9080           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9081           TAPE_ACTIONS_DEFAULT);
9082 }
9083
9084 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9085 {
9086   tape->file_version = getFileVersion(file);
9087   tape->game_version = getFileVersion(file);
9088
9089   return chunk_size;
9090 }
9091
9092 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9093 {
9094   int i;
9095
9096   tape->random_seed = getFile32BitBE(file);
9097   tape->date        = getFile32BitBE(file);
9098   tape->length      = getFile32BitBE(file);
9099
9100   // read header fields that are new since version 1.2
9101   if (tape->file_version >= FILE_VERSION_1_2)
9102   {
9103     byte store_participating_players = getFile8Bit(file);
9104     int engine_version;
9105
9106     // since version 1.2, tapes store which players participate in the tape
9107     tape->num_participating_players = 0;
9108     for (i = 0; i < MAX_PLAYERS; i++)
9109     {
9110       tape->player_participates[i] = FALSE;
9111
9112       if (store_participating_players & (1 << i))
9113       {
9114         tape->player_participates[i] = TRUE;
9115         tape->num_participating_players++;
9116       }
9117     }
9118
9119     setTapeActionFlags(tape, getFile8Bit(file));
9120
9121     tape->property_bits = getFile8Bit(file);
9122     tape->solved = getFile8Bit(file);
9123
9124     engine_version = getFileVersion(file);
9125     if (engine_version > 0)
9126       tape->engine_version = engine_version;
9127     else
9128       tape->engine_version = tape->game_version;
9129   }
9130
9131   return chunk_size;
9132 }
9133
9134 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9135 {
9136   tape->scr_fieldx = getFile8Bit(file);
9137   tape->scr_fieldy = getFile8Bit(file);
9138
9139   return chunk_size;
9140 }
9141
9142 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9143 {
9144   char *level_identifier = NULL;
9145   int level_identifier_size;
9146   int i;
9147
9148   tape->no_info_chunk = FALSE;
9149
9150   level_identifier_size = getFile16BitBE(file);
9151
9152   level_identifier = checked_malloc(level_identifier_size);
9153
9154   for (i = 0; i < level_identifier_size; i++)
9155     level_identifier[i] = getFile8Bit(file);
9156
9157   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9158   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9159
9160   checked_free(level_identifier);
9161
9162   tape->level_nr = getFile16BitBE(file);
9163
9164   chunk_size = 2 + level_identifier_size + 2;
9165
9166   return chunk_size;
9167 }
9168
9169 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9170 {
9171   int i, j;
9172   int tape_pos_size = getTapePosSize(tape);
9173   int chunk_size_expected = tape_pos_size * tape->length;
9174
9175   if (chunk_size_expected != chunk_size)
9176   {
9177     ReadUnusedBytesFromFile(file, chunk_size);
9178     return chunk_size_expected;
9179   }
9180
9181   for (i = 0; i < tape->length; i++)
9182   {
9183     if (i >= MAX_TAPE_LEN)
9184     {
9185       Warn("tape truncated -- size exceeds maximum tape size %d",
9186             MAX_TAPE_LEN);
9187
9188       // tape too large; read and ignore remaining tape data from this chunk
9189       for (;i < tape->length; i++)
9190         ReadUnusedBytesFromFile(file, tape_pos_size);
9191
9192       break;
9193     }
9194
9195     if (tape->use_key_actions)
9196     {
9197       for (j = 0; j < MAX_PLAYERS; j++)
9198       {
9199         tape->pos[i].action[j] = MV_NONE;
9200
9201         if (tape->player_participates[j])
9202           tape->pos[i].action[j] = getFile8Bit(file);
9203       }
9204     }
9205
9206     if (tape->use_mouse_actions)
9207     {
9208       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9209       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9210       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9211     }
9212
9213     tape->pos[i].delay = getFile8Bit(file);
9214
9215     if (tape->file_version == FILE_VERSION_1_0)
9216     {
9217       // eliminate possible diagonal moves in old tapes
9218       // this is only for backward compatibility
9219
9220       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9221       byte action = tape->pos[i].action[0];
9222       int k, num_moves = 0;
9223
9224       for (k = 0; k < 4; k++)
9225       {
9226         if (action & joy_dir[k])
9227         {
9228           tape->pos[i + num_moves].action[0] = joy_dir[k];
9229           if (num_moves > 0)
9230             tape->pos[i + num_moves].delay = 0;
9231           num_moves++;
9232         }
9233       }
9234
9235       if (num_moves > 1)
9236       {
9237         num_moves--;
9238         i += num_moves;
9239         tape->length += num_moves;
9240       }
9241     }
9242     else if (tape->file_version < FILE_VERSION_2_0)
9243     {
9244       // convert pre-2.0 tapes to new tape format
9245
9246       if (tape->pos[i].delay > 1)
9247       {
9248         // action part
9249         tape->pos[i + 1] = tape->pos[i];
9250         tape->pos[i + 1].delay = 1;
9251
9252         // delay part
9253         for (j = 0; j < MAX_PLAYERS; j++)
9254           tape->pos[i].action[j] = MV_NONE;
9255         tape->pos[i].delay--;
9256
9257         i++;
9258         tape->length++;
9259       }
9260     }
9261
9262     if (checkEndOfFile(file))
9263       break;
9264   }
9265
9266   if (i != tape->length)
9267     chunk_size = tape_pos_size * i;
9268
9269   return chunk_size;
9270 }
9271
9272 static void LoadTape_SokobanSolution(char *filename)
9273 {
9274   File *file;
9275   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9276
9277   if (!(file = openFile(filename, MODE_READ)))
9278   {
9279     tape.no_valid_file = TRUE;
9280
9281     return;
9282   }
9283
9284   while (!checkEndOfFile(file))
9285   {
9286     unsigned char c = getByteFromFile(file);
9287
9288     if (checkEndOfFile(file))
9289       break;
9290
9291     switch (c)
9292     {
9293       case 'u':
9294       case 'U':
9295         tape.pos[tape.length].action[0] = MV_UP;
9296         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9297         tape.length++;
9298         break;
9299
9300       case 'd':
9301       case 'D':
9302         tape.pos[tape.length].action[0] = MV_DOWN;
9303         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9304         tape.length++;
9305         break;
9306
9307       case 'l':
9308       case 'L':
9309         tape.pos[tape.length].action[0] = MV_LEFT;
9310         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9311         tape.length++;
9312         break;
9313
9314       case 'r':
9315       case 'R':
9316         tape.pos[tape.length].action[0] = MV_RIGHT;
9317         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9318         tape.length++;
9319         break;
9320
9321       case '\n':
9322       case '\r':
9323       case '\t':
9324       case ' ':
9325         // ignore white-space characters
9326         break;
9327
9328       default:
9329         tape.no_valid_file = TRUE;
9330
9331         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9332
9333         break;
9334     }
9335   }
9336
9337   closeFile(file);
9338
9339   if (tape.no_valid_file)
9340     return;
9341
9342   tape.length_frames  = GetTapeLengthFrames();
9343   tape.length_seconds = GetTapeLengthSeconds();
9344 }
9345
9346 void LoadTapeFromFilename(char *filename)
9347 {
9348   char cookie[MAX_LINE_LEN];
9349   char chunk_name[CHUNK_ID_LEN + 1];
9350   File *file;
9351   int chunk_size;
9352
9353   // always start with reliable default values
9354   setTapeInfoToDefaults();
9355
9356   if (strSuffix(filename, ".sln"))
9357   {
9358     LoadTape_SokobanSolution(filename);
9359
9360     return;
9361   }
9362
9363   if (!(file = openFile(filename, MODE_READ)))
9364   {
9365     tape.no_valid_file = TRUE;
9366
9367     return;
9368   }
9369
9370   getFileChunkBE(file, chunk_name, NULL);
9371   if (strEqual(chunk_name, "RND1"))
9372   {
9373     getFile32BitBE(file);               // not used
9374
9375     getFileChunkBE(file, chunk_name, NULL);
9376     if (!strEqual(chunk_name, "TAPE"))
9377     {
9378       tape.no_valid_file = TRUE;
9379
9380       Warn("unknown format of tape file '%s'", filename);
9381
9382       closeFile(file);
9383
9384       return;
9385     }
9386   }
9387   else  // check for pre-2.0 file format with cookie string
9388   {
9389     strcpy(cookie, chunk_name);
9390     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9391       cookie[4] = '\0';
9392     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9393       cookie[strlen(cookie) - 1] = '\0';
9394
9395     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9396     {
9397       tape.no_valid_file = TRUE;
9398
9399       Warn("unknown format of tape file '%s'", filename);
9400
9401       closeFile(file);
9402
9403       return;
9404     }
9405
9406     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9407     {
9408       tape.no_valid_file = TRUE;
9409
9410       Warn("unsupported version of tape file '%s'", filename);
9411
9412       closeFile(file);
9413
9414       return;
9415     }
9416
9417     // pre-2.0 tape files have no game version, so use file version here
9418     tape.game_version = tape.file_version;
9419   }
9420
9421   if (tape.file_version < FILE_VERSION_1_2)
9422   {
9423     // tape files from versions before 1.2.0 without chunk structure
9424     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9425     LoadTape_BODY(file, 2 * tape.length,      &tape);
9426   }
9427   else
9428   {
9429     static struct
9430     {
9431       char *name;
9432       int size;
9433       int (*loader)(File *, int, struct TapeInfo *);
9434     }
9435     chunk_info[] =
9436     {
9437       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9438       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9439       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9440       { "INFO", -1,                     LoadTape_INFO },
9441       { "BODY", -1,                     LoadTape_BODY },
9442       {  NULL,  0,                      NULL }
9443     };
9444
9445     while (getFileChunkBE(file, chunk_name, &chunk_size))
9446     {
9447       int i = 0;
9448
9449       while (chunk_info[i].name != NULL &&
9450              !strEqual(chunk_name, chunk_info[i].name))
9451         i++;
9452
9453       if (chunk_info[i].name == NULL)
9454       {
9455         Warn("unknown chunk '%s' in tape file '%s'",
9456               chunk_name, filename);
9457
9458         ReadUnusedBytesFromFile(file, chunk_size);
9459       }
9460       else if (chunk_info[i].size != -1 &&
9461                chunk_info[i].size != chunk_size)
9462       {
9463         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9464               chunk_size, chunk_name, filename);
9465
9466         ReadUnusedBytesFromFile(file, chunk_size);
9467       }
9468       else
9469       {
9470         // call function to load this tape chunk
9471         int chunk_size_expected =
9472           (chunk_info[i].loader)(file, chunk_size, &tape);
9473
9474         // the size of some chunks cannot be checked before reading other
9475         // chunks first (like "HEAD" and "BODY") that contain some header
9476         // information, so check them here
9477         if (chunk_size_expected != chunk_size)
9478         {
9479           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9480                 chunk_size, chunk_name, filename);
9481         }
9482       }
9483     }
9484   }
9485
9486   closeFile(file);
9487
9488   tape.length_frames  = GetTapeLengthFrames();
9489   tape.length_seconds = GetTapeLengthSeconds();
9490
9491 #if 0
9492   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9493         tape.file_version);
9494   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9495         tape.game_version);
9496   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9497         tape.engine_version);
9498 #endif
9499 }
9500
9501 void LoadTape(int nr)
9502 {
9503   char *filename = getTapeFilename(nr);
9504
9505   LoadTapeFromFilename(filename);
9506 }
9507
9508 void LoadSolutionTape(int nr)
9509 {
9510   char *filename = getSolutionTapeFilename(nr);
9511
9512   LoadTapeFromFilename(filename);
9513
9514   if (TAPE_IS_EMPTY(tape))
9515   {
9516     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9517         level.native_bd_level->replay != NULL)
9518       CopyNativeTape_BD_to_RND(&level);
9519     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9520         level.native_sp_level->demo.is_available)
9521       CopyNativeTape_SP_to_RND(&level);
9522   }
9523 }
9524
9525 void LoadScoreTape(char *score_tape_basename, int nr)
9526 {
9527   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9528
9529   LoadTapeFromFilename(filename);
9530 }
9531
9532 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9533 {
9534   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9535
9536   LoadTapeFromFilename(filename);
9537 }
9538
9539 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9540 {
9541   // chunk required for team mode tapes with non-default screen size
9542   return (tape->num_participating_players > 1 &&
9543           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9544            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9545 }
9546
9547 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9548 {
9549   putFileVersion(file, tape->file_version);
9550   putFileVersion(file, tape->game_version);
9551 }
9552
9553 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9554 {
9555   int i;
9556   byte store_participating_players = 0;
9557
9558   // set bits for participating players for compact storage
9559   for (i = 0; i < MAX_PLAYERS; i++)
9560     if (tape->player_participates[i])
9561       store_participating_players |= (1 << i);
9562
9563   putFile32BitBE(file, tape->random_seed);
9564   putFile32BitBE(file, tape->date);
9565   putFile32BitBE(file, tape->length);
9566
9567   putFile8Bit(file, store_participating_players);
9568
9569   putFile8Bit(file, getTapeActionValue(tape));
9570
9571   putFile8Bit(file, tape->property_bits);
9572   putFile8Bit(file, tape->solved);
9573
9574   putFileVersion(file, tape->engine_version);
9575 }
9576
9577 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9578 {
9579   putFile8Bit(file, tape->scr_fieldx);
9580   putFile8Bit(file, tape->scr_fieldy);
9581 }
9582
9583 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9584 {
9585   int level_identifier_size = strlen(tape->level_identifier) + 1;
9586   int i;
9587
9588   putFile16BitBE(file, level_identifier_size);
9589
9590   for (i = 0; i < level_identifier_size; i++)
9591     putFile8Bit(file, tape->level_identifier[i]);
9592
9593   putFile16BitBE(file, tape->level_nr);
9594 }
9595
9596 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9597 {
9598   int i, j;
9599
9600   for (i = 0; i < tape->length; i++)
9601   {
9602     if (tape->use_key_actions)
9603     {
9604       for (j = 0; j < MAX_PLAYERS; j++)
9605         if (tape->player_participates[j])
9606           putFile8Bit(file, tape->pos[i].action[j]);
9607     }
9608
9609     if (tape->use_mouse_actions)
9610     {
9611       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9612       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9613       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9614     }
9615
9616     putFile8Bit(file, tape->pos[i].delay);
9617   }
9618 }
9619
9620 void SaveTapeToFilename(char *filename)
9621 {
9622   FILE *file;
9623   int tape_pos_size;
9624   int info_chunk_size;
9625   int body_chunk_size;
9626
9627   if (!(file = fopen(filename, MODE_WRITE)))
9628   {
9629     Warn("cannot save level recording file '%s'", filename);
9630
9631     return;
9632   }
9633
9634   tape_pos_size = getTapePosSize(&tape);
9635
9636   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9637   body_chunk_size = tape_pos_size * tape.length;
9638
9639   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9640   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9641
9642   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9643   SaveTape_VERS(file, &tape);
9644
9645   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9646   SaveTape_HEAD(file, &tape);
9647
9648   if (checkSaveTape_SCRN(&tape))
9649   {
9650     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9651     SaveTape_SCRN(file, &tape);
9652   }
9653
9654   putFileChunkBE(file, "INFO", info_chunk_size);
9655   SaveTape_INFO(file, &tape);
9656
9657   putFileChunkBE(file, "BODY", body_chunk_size);
9658   SaveTape_BODY(file, &tape);
9659
9660   fclose(file);
9661
9662   SetFilePermissions(filename, PERMS_PRIVATE);
9663 }
9664
9665 static void SaveTapeExt(char *filename)
9666 {
9667   int i;
9668
9669   tape.file_version = FILE_VERSION_ACTUAL;
9670   tape.game_version = GAME_VERSION_ACTUAL;
9671
9672   tape.num_participating_players = 0;
9673
9674   // count number of participating players
9675   for (i = 0; i < MAX_PLAYERS; i++)
9676     if (tape.player_participates[i])
9677       tape.num_participating_players++;
9678
9679   SaveTapeToFilename(filename);
9680
9681   tape.changed = FALSE;
9682 }
9683
9684 void SaveTape(int nr)
9685 {
9686   char *filename = getTapeFilename(nr);
9687
9688   InitTapeDirectory(leveldir_current->subdir);
9689
9690   SaveTapeExt(filename);
9691 }
9692
9693 void SaveScoreTape(int nr)
9694 {
9695   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9696
9697   // used instead of "leveldir_current->subdir" (for network games)
9698   InitScoreTapeDirectory(levelset.identifier, nr);
9699
9700   SaveTapeExt(filename);
9701 }
9702
9703 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9704                                   unsigned int req_state_added)
9705 {
9706   char *filename = getTapeFilename(nr);
9707   boolean new_tape = !fileExists(filename);
9708   boolean tape_saved = FALSE;
9709
9710   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9711   {
9712     SaveTape(nr);
9713
9714     if (new_tape)
9715       Request(msg_saved, REQ_CONFIRM | req_state_added);
9716
9717     tape_saved = TRUE;
9718   }
9719
9720   return tape_saved;
9721 }
9722
9723 boolean SaveTapeChecked(int nr)
9724 {
9725   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9726 }
9727
9728 boolean SaveTapeChecked_LevelSolved(int nr)
9729 {
9730   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9731                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9732 }
9733
9734 void DumpTape(struct TapeInfo *tape)
9735 {
9736   int tape_frame_counter;
9737   int i, j;
9738
9739   if (tape->no_valid_file)
9740   {
9741     Warn("cannot dump -- no valid tape file found");
9742
9743     return;
9744   }
9745
9746   PrintLine("-", 79);
9747
9748   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9749         tape->level_nr, tape->file_version, tape->game_version);
9750   Print("                  (effective engine version %08d)\n",
9751         tape->engine_version);
9752   Print("Level series identifier: '%s'\n", tape->level_identifier);
9753
9754   Print("Solution tape: %s\n",
9755         tape->solved ? "yes" :
9756         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9757
9758   Print("Special tape properties: ");
9759   if (tape->property_bits == TAPE_PROPERTY_NONE)
9760     Print("[none]");
9761   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9762     Print("[em_random_bug]");
9763   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9764     Print("[game_speed]");
9765   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9766     Print("[pause]");
9767   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9768     Print("[single_step]");
9769   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9770     Print("[snapshot]");
9771   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9772     Print("[replayed]");
9773   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9774     Print("[tas_keys]");
9775   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9776     Print("[small_graphics]");
9777   Print("\n");
9778
9779   int year2 = tape->date / 10000;
9780   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9781   int month_index_raw = (tape->date / 100) % 100;
9782   int month_index = month_index_raw % 12;       // prevent invalid index
9783   int month = month_index + 1;
9784   int day = tape->date % 100;
9785
9786   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9787
9788   PrintLine("-", 79);
9789
9790   tape_frame_counter = 0;
9791
9792   for (i = 0; i < tape->length; i++)
9793   {
9794     if (i >= MAX_TAPE_LEN)
9795       break;
9796
9797     Print("%04d: ", i);
9798
9799     for (j = 0; j < MAX_PLAYERS; j++)
9800     {
9801       if (tape->player_participates[j])
9802       {
9803         int action = tape->pos[i].action[j];
9804
9805         Print("%d:%02x ", j, action);
9806         Print("[%c%c%c%c|%c%c] - ",
9807               (action & JOY_LEFT ? '<' : ' '),
9808               (action & JOY_RIGHT ? '>' : ' '),
9809               (action & JOY_UP ? '^' : ' '),
9810               (action & JOY_DOWN ? 'v' : ' '),
9811               (action & JOY_BUTTON_1 ? '1' : ' '),
9812               (action & JOY_BUTTON_2 ? '2' : ' '));
9813       }
9814     }
9815
9816     Print("(%03d) ", tape->pos[i].delay);
9817     Print("[%05d]\n", tape_frame_counter);
9818
9819     tape_frame_counter += tape->pos[i].delay;
9820   }
9821
9822   PrintLine("-", 79);
9823 }
9824
9825 void DumpTapes(void)
9826 {
9827   static LevelDirTree *dumptape_leveldir = NULL;
9828
9829   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9830                                                 global.dumptape_leveldir);
9831
9832   if (dumptape_leveldir == NULL)
9833     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9834
9835   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9836       global.dumptape_level_nr > dumptape_leveldir->last_level)
9837     Fail("no such level number: %d", global.dumptape_level_nr);
9838
9839   leveldir_current = dumptape_leveldir;
9840
9841   if (options.mytapes)
9842     LoadTape(global.dumptape_level_nr);
9843   else
9844     LoadSolutionTape(global.dumptape_level_nr);
9845
9846   DumpTape(&tape);
9847
9848   CloseAllAndExit(0);
9849 }
9850
9851
9852 // ============================================================================
9853 // score file functions
9854 // ============================================================================
9855
9856 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9857 {
9858   int i;
9859
9860   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9861   {
9862     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9863     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9864     scores->entry[i].score = 0;
9865     scores->entry[i].time = 0;
9866
9867     scores->entry[i].id = -1;
9868     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9869     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9870     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9871     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9872     strcpy(scores->entry[i].country_code, "??");
9873   }
9874
9875   scores->num_entries = 0;
9876   scores->last_added = -1;
9877   scores->last_added_local = -1;
9878
9879   scores->updated = FALSE;
9880   scores->uploaded = FALSE;
9881   scores->tape_downloaded = FALSE;
9882   scores->force_last_added = FALSE;
9883
9884   // The following values are intentionally not reset here:
9885   // - last_level_nr
9886   // - last_entry_nr
9887   // - next_level_nr
9888   // - continue_playing
9889   // - continue_on_return
9890 }
9891
9892 static void setScoreInfoToDefaults(void)
9893 {
9894   setScoreInfoToDefaultsExt(&scores);
9895 }
9896
9897 static void setServerScoreInfoToDefaults(void)
9898 {
9899   setScoreInfoToDefaultsExt(&server_scores);
9900 }
9901
9902 static void LoadScore_OLD(int nr)
9903 {
9904   int i;
9905   char *filename = getScoreFilename(nr);
9906   char cookie[MAX_LINE_LEN];
9907   char line[MAX_LINE_LEN];
9908   char *line_ptr;
9909   FILE *file;
9910
9911   if (!(file = fopen(filename, MODE_READ)))
9912     return;
9913
9914   // check file identifier
9915   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9916     cookie[0] = '\0';
9917   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9918     cookie[strlen(cookie) - 1] = '\0';
9919
9920   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9921   {
9922     Warn("unknown format of score file '%s'", filename);
9923
9924     fclose(file);
9925
9926     return;
9927   }
9928
9929   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9930   {
9931     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9932       Warn("fscanf() failed; %s", strerror(errno));
9933
9934     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9935       line[0] = '\0';
9936
9937     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9938       line[strlen(line) - 1] = '\0';
9939
9940     for (line_ptr = line; *line_ptr; line_ptr++)
9941     {
9942       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9943       {
9944         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9945         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9946         break;
9947       }
9948     }
9949   }
9950
9951   fclose(file);
9952 }
9953
9954 static void ConvertScore_OLD(void)
9955 {
9956   // only convert score to time for levels that rate playing time over score
9957   if (!level.rate_time_over_score)
9958     return;
9959
9960   // convert old score to playing time for score-less levels (like Supaplex)
9961   int time_final_max = 999;
9962   int i;
9963
9964   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9965   {
9966     int score = scores.entry[i].score;
9967
9968     if (score > 0 && score < time_final_max)
9969       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9970   }
9971 }
9972
9973 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9974 {
9975   scores->file_version = getFileVersion(file);
9976   scores->game_version = getFileVersion(file);
9977
9978   return chunk_size;
9979 }
9980
9981 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9982 {
9983   char *level_identifier = NULL;
9984   int level_identifier_size;
9985   int i;
9986
9987   level_identifier_size = getFile16BitBE(file);
9988
9989   level_identifier = checked_malloc(level_identifier_size);
9990
9991   for (i = 0; i < level_identifier_size; i++)
9992     level_identifier[i] = getFile8Bit(file);
9993
9994   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9995   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9996
9997   checked_free(level_identifier);
9998
9999   scores->level_nr = getFile16BitBE(file);
10000   scores->num_entries = getFile16BitBE(file);
10001
10002   chunk_size = 2 + level_identifier_size + 2 + 2;
10003
10004   return chunk_size;
10005 }
10006
10007 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10008 {
10009   int i, j;
10010
10011   for (i = 0; i < scores->num_entries; i++)
10012   {
10013     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10014       scores->entry[i].name[j] = getFile8Bit(file);
10015
10016     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10017   }
10018
10019   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10020
10021   return chunk_size;
10022 }
10023
10024 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10025 {
10026   int i;
10027
10028   for (i = 0; i < scores->num_entries; i++)
10029     scores->entry[i].score = getFile16BitBE(file);
10030
10031   chunk_size = scores->num_entries * 2;
10032
10033   return chunk_size;
10034 }
10035
10036 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10037 {
10038   int i;
10039
10040   for (i = 0; i < scores->num_entries; i++)
10041     scores->entry[i].score = getFile32BitBE(file);
10042
10043   chunk_size = scores->num_entries * 4;
10044
10045   return chunk_size;
10046 }
10047
10048 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10049 {
10050   int i;
10051
10052   for (i = 0; i < scores->num_entries; i++)
10053     scores->entry[i].time = getFile32BitBE(file);
10054
10055   chunk_size = scores->num_entries * 4;
10056
10057   return chunk_size;
10058 }
10059
10060 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10061 {
10062   int i, j;
10063
10064   for (i = 0; i < scores->num_entries; i++)
10065   {
10066     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10067       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10068
10069     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10070   }
10071
10072   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10073
10074   return chunk_size;
10075 }
10076
10077 void LoadScore(int nr)
10078 {
10079   char *filename = getScoreFilename(nr);
10080   char cookie[MAX_LINE_LEN];
10081   char chunk_name[CHUNK_ID_LEN + 1];
10082   int chunk_size;
10083   boolean old_score_file_format = FALSE;
10084   File *file;
10085
10086   // always start with reliable default values
10087   setScoreInfoToDefaults();
10088
10089   if (!(file = openFile(filename, MODE_READ)))
10090     return;
10091
10092   getFileChunkBE(file, chunk_name, NULL);
10093   if (strEqual(chunk_name, "RND1"))
10094   {
10095     getFile32BitBE(file);               // not used
10096
10097     getFileChunkBE(file, chunk_name, NULL);
10098     if (!strEqual(chunk_name, "SCOR"))
10099     {
10100       Warn("unknown format of score file '%s'", filename);
10101
10102       closeFile(file);
10103
10104       return;
10105     }
10106   }
10107   else  // check for old file format with cookie string
10108   {
10109     strcpy(cookie, chunk_name);
10110     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10111       cookie[4] = '\0';
10112     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10113       cookie[strlen(cookie) - 1] = '\0';
10114
10115     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10116     {
10117       Warn("unknown format of score file '%s'", filename);
10118
10119       closeFile(file);
10120
10121       return;
10122     }
10123
10124     old_score_file_format = TRUE;
10125   }
10126
10127   if (old_score_file_format)
10128   {
10129     // score files from versions before 4.2.4.0 without chunk structure
10130     LoadScore_OLD(nr);
10131
10132     // convert score to time, if possible (mainly for Supaplex levels)
10133     ConvertScore_OLD();
10134   }
10135   else
10136   {
10137     static struct
10138     {
10139       char *name;
10140       int size;
10141       int (*loader)(File *, int, struct ScoreInfo *);
10142     }
10143     chunk_info[] =
10144     {
10145       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10146       { "INFO", -1,                     LoadScore_INFO },
10147       { "NAME", -1,                     LoadScore_NAME },
10148       { "SCOR", -1,                     LoadScore_SCOR },
10149       { "SC4R", -1,                     LoadScore_SC4R },
10150       { "TIME", -1,                     LoadScore_TIME },
10151       { "TAPE", -1,                     LoadScore_TAPE },
10152
10153       {  NULL,  0,                      NULL }
10154     };
10155
10156     while (getFileChunkBE(file, chunk_name, &chunk_size))
10157     {
10158       int i = 0;
10159
10160       while (chunk_info[i].name != NULL &&
10161              !strEqual(chunk_name, chunk_info[i].name))
10162         i++;
10163
10164       if (chunk_info[i].name == NULL)
10165       {
10166         Warn("unknown chunk '%s' in score file '%s'",
10167               chunk_name, filename);
10168
10169         ReadUnusedBytesFromFile(file, chunk_size);
10170       }
10171       else if (chunk_info[i].size != -1 &&
10172                chunk_info[i].size != chunk_size)
10173       {
10174         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10175               chunk_size, chunk_name, filename);
10176
10177         ReadUnusedBytesFromFile(file, chunk_size);
10178       }
10179       else
10180       {
10181         // call function to load this score chunk
10182         int chunk_size_expected =
10183           (chunk_info[i].loader)(file, chunk_size, &scores);
10184
10185         // the size of some chunks cannot be checked before reading other
10186         // chunks first (like "HEAD" and "BODY") that contain some header
10187         // information, so check them here
10188         if (chunk_size_expected != chunk_size)
10189         {
10190           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10191                 chunk_size, chunk_name, filename);
10192         }
10193       }
10194     }
10195   }
10196
10197   closeFile(file);
10198 }
10199
10200 #if ENABLE_HISTORIC_CHUNKS
10201 void SaveScore_OLD(int nr)
10202 {
10203   int i;
10204   char *filename = getScoreFilename(nr);
10205   FILE *file;
10206
10207   // used instead of "leveldir_current->subdir" (for network games)
10208   InitScoreDirectory(levelset.identifier);
10209
10210   if (!(file = fopen(filename, MODE_WRITE)))
10211   {
10212     Warn("cannot save score for level %d", nr);
10213
10214     return;
10215   }
10216
10217   fprintf(file, "%s\n\n", SCORE_COOKIE);
10218
10219   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10220     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10221
10222   fclose(file);
10223
10224   SetFilePermissions(filename, PERMS_PRIVATE);
10225 }
10226 #endif
10227
10228 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10229 {
10230   putFileVersion(file, scores->file_version);
10231   putFileVersion(file, scores->game_version);
10232 }
10233
10234 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10235 {
10236   int level_identifier_size = strlen(scores->level_identifier) + 1;
10237   int i;
10238
10239   putFile16BitBE(file, level_identifier_size);
10240
10241   for (i = 0; i < level_identifier_size; i++)
10242     putFile8Bit(file, scores->level_identifier[i]);
10243
10244   putFile16BitBE(file, scores->level_nr);
10245   putFile16BitBE(file, scores->num_entries);
10246 }
10247
10248 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10249 {
10250   int i, j;
10251
10252   for (i = 0; i < scores->num_entries; i++)
10253   {
10254     int name_size = strlen(scores->entry[i].name);
10255
10256     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10257       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10258   }
10259 }
10260
10261 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10262 {
10263   int i;
10264
10265   for (i = 0; i < scores->num_entries; i++)
10266     putFile16BitBE(file, scores->entry[i].score);
10267 }
10268
10269 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10270 {
10271   int i;
10272
10273   for (i = 0; i < scores->num_entries; i++)
10274     putFile32BitBE(file, scores->entry[i].score);
10275 }
10276
10277 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10278 {
10279   int i;
10280
10281   for (i = 0; i < scores->num_entries; i++)
10282     putFile32BitBE(file, scores->entry[i].time);
10283 }
10284
10285 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10286 {
10287   int i, j;
10288
10289   for (i = 0; i < scores->num_entries; i++)
10290   {
10291     int size = strlen(scores->entry[i].tape_basename);
10292
10293     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10294       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10295   }
10296 }
10297
10298 static void SaveScoreToFilename(char *filename)
10299 {
10300   FILE *file;
10301   int info_chunk_size;
10302   int name_chunk_size;
10303   int scor_chunk_size;
10304   int sc4r_chunk_size;
10305   int time_chunk_size;
10306   int tape_chunk_size;
10307   boolean has_large_score_values;
10308   int i;
10309
10310   if (!(file = fopen(filename, MODE_WRITE)))
10311   {
10312     Warn("cannot save score file '%s'", filename);
10313
10314     return;
10315   }
10316
10317   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10318   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10319   scor_chunk_size = scores.num_entries * 2;
10320   sc4r_chunk_size = scores.num_entries * 4;
10321   time_chunk_size = scores.num_entries * 4;
10322   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10323
10324   has_large_score_values = FALSE;
10325   for (i = 0; i < scores.num_entries; i++)
10326     if (scores.entry[i].score > 0xffff)
10327       has_large_score_values = TRUE;
10328
10329   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10330   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10331
10332   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10333   SaveScore_VERS(file, &scores);
10334
10335   putFileChunkBE(file, "INFO", info_chunk_size);
10336   SaveScore_INFO(file, &scores);
10337
10338   putFileChunkBE(file, "NAME", name_chunk_size);
10339   SaveScore_NAME(file, &scores);
10340
10341   if (has_large_score_values)
10342   {
10343     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10344     SaveScore_SC4R(file, &scores);
10345   }
10346   else
10347   {
10348     putFileChunkBE(file, "SCOR", scor_chunk_size);
10349     SaveScore_SCOR(file, &scores);
10350   }
10351
10352   putFileChunkBE(file, "TIME", time_chunk_size);
10353   SaveScore_TIME(file, &scores);
10354
10355   putFileChunkBE(file, "TAPE", tape_chunk_size);
10356   SaveScore_TAPE(file, &scores);
10357
10358   fclose(file);
10359
10360   SetFilePermissions(filename, PERMS_PRIVATE);
10361 }
10362
10363 void SaveScore(int nr)
10364 {
10365   char *filename = getScoreFilename(nr);
10366   int i;
10367
10368   // used instead of "leveldir_current->subdir" (for network games)
10369   InitScoreDirectory(levelset.identifier);
10370
10371   scores.file_version = FILE_VERSION_ACTUAL;
10372   scores.game_version = GAME_VERSION_ACTUAL;
10373
10374   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10375   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10376   scores.level_nr = level_nr;
10377
10378   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10379     if (scores.entry[i].score == 0 &&
10380         scores.entry[i].time == 0 &&
10381         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10382       break;
10383
10384   scores.num_entries = i;
10385
10386   if (scores.num_entries == 0)
10387     return;
10388
10389   SaveScoreToFilename(filename);
10390 }
10391
10392 static void LoadServerScoreFromCache(int nr)
10393 {
10394   struct ScoreEntry score_entry;
10395   struct
10396   {
10397     void *value;
10398     boolean is_string;
10399     int string_size;
10400   }
10401   score_mapping[] =
10402   {
10403     { &score_entry.score,               FALSE,  0                       },
10404     { &score_entry.time,                FALSE,  0                       },
10405     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10406     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10407     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10408     { &score_entry.id,                  FALSE,  0                       },
10409     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10410     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10411     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10412     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10413
10414     { NULL,                             FALSE,  0                       }
10415   };
10416   char *filename = getScoreCacheFilename(nr);
10417   SetupFileHash *score_hash = loadSetupFileHash(filename);
10418   int i, j;
10419
10420   server_scores.num_entries = 0;
10421
10422   if (score_hash == NULL)
10423     return;
10424
10425   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10426   {
10427     score_entry = server_scores.entry[i];
10428
10429     for (j = 0; score_mapping[j].value != NULL; j++)
10430     {
10431       char token[10];
10432
10433       sprintf(token, "%02d.%d", i, j);
10434
10435       char *value = getHashEntry(score_hash, token);
10436
10437       if (value == NULL)
10438         continue;
10439
10440       if (score_mapping[j].is_string)
10441       {
10442         char *score_value = (char *)score_mapping[j].value;
10443         int value_size = score_mapping[j].string_size;
10444
10445         strncpy(score_value, value, value_size);
10446         score_value[value_size] = '\0';
10447       }
10448       else
10449       {
10450         int *score_value = (int *)score_mapping[j].value;
10451
10452         *score_value = atoi(value);
10453       }
10454
10455       server_scores.num_entries = i + 1;
10456     }
10457
10458     server_scores.entry[i] = score_entry;
10459   }
10460
10461   freeSetupFileHash(score_hash);
10462 }
10463
10464 void LoadServerScore(int nr, boolean download_score)
10465 {
10466   if (!setup.use_api_server)
10467     return;
10468
10469   // always start with reliable default values
10470   setServerScoreInfoToDefaults();
10471
10472   // 1st step: load server scores from cache file (which may not exist)
10473   // (this should prevent reading it while the thread is writing to it)
10474   LoadServerScoreFromCache(nr);
10475
10476   if (download_score && runtime.use_api_server)
10477   {
10478     // 2nd step: download server scores from score server to cache file
10479     // (as thread, as it might time out if the server is not reachable)
10480     ApiGetScoreAsThread(nr);
10481   }
10482 }
10483
10484 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10485 {
10486   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10487
10488   // if score tape not uploaded, ask for uploading missing tapes later
10489   if (!setup.has_remaining_tapes)
10490     setup.ask_for_remaining_tapes = TRUE;
10491
10492   setup.provide_uploading_tapes = TRUE;
10493   setup.has_remaining_tapes = TRUE;
10494
10495   SaveSetup_ServerSetup();
10496 }
10497
10498 void SaveServerScore(int nr, boolean tape_saved)
10499 {
10500   if (!runtime.use_api_server)
10501   {
10502     PrepareScoreTapesForUpload(leveldir_current->subdir);
10503
10504     return;
10505   }
10506
10507   ApiAddScoreAsThread(nr, tape_saved, NULL);
10508 }
10509
10510 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10511                              char *score_tape_filename)
10512 {
10513   if (!runtime.use_api_server)
10514     return;
10515
10516   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10517 }
10518
10519 void LoadLocalAndServerScore(int nr, boolean download_score)
10520 {
10521   int last_added_local = scores.last_added_local;
10522   boolean force_last_added = scores.force_last_added;
10523
10524   // needed if only showing server scores
10525   setScoreInfoToDefaults();
10526
10527   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10528     LoadScore(nr);
10529
10530   // restore last added local score entry (before merging server scores)
10531   scores.last_added = scores.last_added_local = last_added_local;
10532
10533   if (setup.use_api_server &&
10534       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10535   {
10536     // load server scores from cache file and trigger update from server
10537     LoadServerScore(nr, download_score);
10538
10539     // merge local scores with scores from server
10540     MergeServerScore();
10541   }
10542
10543   if (force_last_added)
10544     scores.force_last_added = force_last_added;
10545 }
10546
10547
10548 // ============================================================================
10549 // setup file functions
10550 // ============================================================================
10551
10552 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10553
10554
10555 static struct TokenInfo global_setup_tokens[] =
10556 {
10557   {
10558     TYPE_STRING,
10559     &setup.player_name,                         "player_name"
10560   },
10561   {
10562     TYPE_SWITCH,
10563     &setup.multiple_users,                      "multiple_users"
10564   },
10565   {
10566     TYPE_SWITCH,
10567     &setup.sound,                               "sound"
10568   },
10569   {
10570     TYPE_SWITCH,
10571     &setup.sound_loops,                         "repeating_sound_loops"
10572   },
10573   {
10574     TYPE_SWITCH,
10575     &setup.sound_music,                         "background_music"
10576   },
10577   {
10578     TYPE_SWITCH,
10579     &setup.sound_simple,                        "simple_sound_effects"
10580   },
10581   {
10582     TYPE_SWITCH,
10583     &setup.toons,                               "toons"
10584   },
10585   {
10586     TYPE_SWITCH,
10587     &setup.global_animations,                   "global_animations"
10588   },
10589   {
10590     TYPE_SWITCH,
10591     &setup.scroll_delay,                        "scroll_delay"
10592   },
10593   {
10594     TYPE_SWITCH,
10595     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10596   },
10597   {
10598     TYPE_INTEGER,
10599     &setup.scroll_delay_value,                  "scroll_delay_value"
10600   },
10601   {
10602     TYPE_STRING,
10603     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10604   },
10605   {
10606     TYPE_INTEGER,
10607     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10608   },
10609   {
10610     TYPE_SWITCH,
10611     &setup.fade_screens,                        "fade_screens"
10612   },
10613   {
10614     TYPE_SWITCH,
10615     &setup.autorecord,                          "automatic_tape_recording"
10616   },
10617   {
10618     TYPE_SWITCH,
10619     &setup.autorecord_after_replay,             "autorecord_after_replay"
10620   },
10621   {
10622     TYPE_SWITCH,
10623     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10624   },
10625   {
10626     TYPE_SWITCH,
10627     &setup.show_titlescreen,                    "show_titlescreen"
10628   },
10629   {
10630     TYPE_SWITCH,
10631     &setup.quick_doors,                         "quick_doors"
10632   },
10633   {
10634     TYPE_SWITCH,
10635     &setup.team_mode,                           "team_mode"
10636   },
10637   {
10638     TYPE_SWITCH,
10639     &setup.handicap,                            "handicap"
10640   },
10641   {
10642     TYPE_SWITCH,
10643     &setup.skip_levels,                         "skip_levels"
10644   },
10645   {
10646     TYPE_SWITCH,
10647     &setup.increment_levels,                    "increment_levels"
10648   },
10649   {
10650     TYPE_SWITCH,
10651     &setup.auto_play_next_level,                "auto_play_next_level"
10652   },
10653   {
10654     TYPE_SWITCH,
10655     &setup.count_score_after_game,              "count_score_after_game"
10656   },
10657   {
10658     TYPE_SWITCH,
10659     &setup.show_scores_after_game,              "show_scores_after_game"
10660   },
10661   {
10662     TYPE_SWITCH,
10663     &setup.time_limit,                          "time_limit"
10664   },
10665   {
10666     TYPE_SWITCH,
10667     &setup.fullscreen,                          "fullscreen"
10668   },
10669   {
10670     TYPE_INTEGER,
10671     &setup.window_scaling_percent,              "window_scaling_percent"
10672   },
10673   {
10674     TYPE_STRING,
10675     &setup.window_scaling_quality,              "window_scaling_quality"
10676   },
10677   {
10678     TYPE_STRING,
10679     &setup.screen_rendering_mode,               "screen_rendering_mode"
10680   },
10681   {
10682     TYPE_STRING,
10683     &setup.vsync_mode,                          "vsync_mode"
10684   },
10685   {
10686     TYPE_SWITCH,
10687     &setup.ask_on_escape,                       "ask_on_escape"
10688   },
10689   {
10690     TYPE_SWITCH,
10691     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10692   },
10693   {
10694     TYPE_SWITCH,
10695     &setup.ask_on_game_over,                    "ask_on_game_over"
10696   },
10697   {
10698     TYPE_SWITCH,
10699     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10700   },
10701   {
10702     TYPE_SWITCH,
10703     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10704   },
10705   {
10706     TYPE_SWITCH,
10707     &setup.quick_switch,                        "quick_player_switch"
10708   },
10709   {
10710     TYPE_SWITCH,
10711     &setup.input_on_focus,                      "input_on_focus"
10712   },
10713   {
10714     TYPE_SWITCH,
10715     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10716   },
10717   {
10718     TYPE_SWITCH,
10719     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10720   },
10721   {
10722     TYPE_SWITCH,
10723     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10724   },
10725   {
10726     TYPE_SWITCH,
10727     &setup.game_speed_extended,                 "game_speed_extended"
10728   },
10729   {
10730     TYPE_INTEGER,
10731     &setup.game_frame_delay,                    "game_frame_delay"
10732   },
10733   {
10734     TYPE_INTEGER,
10735     &setup.default_game_engine_type,            "default_game_engine_type"
10736   },
10737   {
10738     TYPE_SWITCH,
10739     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10740   },
10741   {
10742     TYPE_SWITCH,
10743     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10744   },
10745   {
10746     TYPE_SWITCH,
10747     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10748   },
10749   {
10750     TYPE_SWITCH3,
10751     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10752   },
10753   {
10754     TYPE_INTEGER,
10755     &setup.bd_palette_c64,                      "bd_palette_c64"
10756   },
10757   {
10758     TYPE_INTEGER,
10759     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10760   },
10761   {
10762     TYPE_INTEGER,
10763     &setup.bd_palette_atari,                    "bd_palette_atari"
10764   },
10765   {
10766     TYPE_INTEGER,
10767     &setup.bd_default_color_type,               "bd_default_color_type"
10768   },
10769   {
10770     TYPE_SWITCH,
10771     &setup.sp_show_border_elements,             "sp_show_border_elements"
10772   },
10773   {
10774     TYPE_SWITCH,
10775     &setup.small_game_graphics,                 "small_game_graphics"
10776   },
10777   {
10778     TYPE_SWITCH,
10779     &setup.show_load_save_buttons,              "show_load_save_buttons"
10780   },
10781   {
10782     TYPE_SWITCH,
10783     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10784   },
10785   {
10786     TYPE_STRING,
10787     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10788   },
10789   {
10790     TYPE_STRING,
10791     &setup.graphics_set,                        "graphics_set"
10792   },
10793   {
10794     TYPE_STRING,
10795     &setup.sounds_set,                          "sounds_set"
10796   },
10797   {
10798     TYPE_STRING,
10799     &setup.music_set,                           "music_set"
10800   },
10801   {
10802     TYPE_SWITCH3,
10803     &setup.override_level_graphics,             "override_level_graphics"
10804   },
10805   {
10806     TYPE_SWITCH3,
10807     &setup.override_level_sounds,               "override_level_sounds"
10808   },
10809   {
10810     TYPE_SWITCH3,
10811     &setup.override_level_music,                "override_level_music"
10812   },
10813   {
10814     TYPE_INTEGER,
10815     &setup.volume_simple,                       "volume_simple"
10816   },
10817   {
10818     TYPE_INTEGER,
10819     &setup.volume_loops,                        "volume_loops"
10820   },
10821   {
10822     TYPE_INTEGER,
10823     &setup.volume_music,                        "volume_music"
10824   },
10825   {
10826     TYPE_SWITCH,
10827     &setup.network_mode,                        "network_mode"
10828   },
10829   {
10830     TYPE_PLAYER,
10831     &setup.network_player_nr,                   "network_player"
10832   },
10833   {
10834     TYPE_STRING,
10835     &setup.network_server_hostname,             "network_server_hostname"
10836   },
10837   {
10838     TYPE_STRING,
10839     &setup.touch.control_type,                  "touch.control_type"
10840   },
10841   {
10842     TYPE_INTEGER,
10843     &setup.touch.move_distance,                 "touch.move_distance"
10844   },
10845   {
10846     TYPE_INTEGER,
10847     &setup.touch.drop_distance,                 "touch.drop_distance"
10848   },
10849   {
10850     TYPE_INTEGER,
10851     &setup.touch.transparency,                  "touch.transparency"
10852   },
10853   {
10854     TYPE_INTEGER,
10855     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10856   },
10857   {
10858     TYPE_INTEGER,
10859     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10860   },
10861   {
10862     TYPE_INTEGER,
10863     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10864   },
10865   {
10866     TYPE_INTEGER,
10867     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10868   },
10869   {
10870     TYPE_INTEGER,
10871     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10872   },
10873   {
10874     TYPE_INTEGER,
10875     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10876   },
10877   {
10878     TYPE_SWITCH,
10879     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10880   },
10881 };
10882
10883 static struct TokenInfo auto_setup_tokens[] =
10884 {
10885   {
10886     TYPE_INTEGER,
10887     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10888   },
10889 };
10890
10891 static struct TokenInfo server_setup_tokens[] =
10892 {
10893   {
10894     TYPE_STRING,
10895     &setup.player_uuid,                         "player_uuid"
10896   },
10897   {
10898     TYPE_INTEGER,
10899     &setup.player_version,                      "player_version"
10900   },
10901   {
10902     TYPE_SWITCH,
10903     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10904   },
10905   {
10906     TYPE_STRING,
10907     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10908   },
10909   {
10910     TYPE_STRING,
10911     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10912   },
10913   {
10914     TYPE_SWITCH,
10915     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10916   },
10917   {
10918     TYPE_SWITCH,
10919     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10920   },
10921   {
10922     TYPE_SWITCH,
10923     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10924   },
10925   {
10926     TYPE_SWITCH,
10927     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10928   },
10929   {
10930     TYPE_SWITCH,
10931     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10932   },
10933 };
10934
10935 static struct TokenInfo editor_setup_tokens[] =
10936 {
10937   {
10938     TYPE_SWITCH,
10939     &setup.editor.el_classic,                   "editor.el_classic"
10940   },
10941   {
10942     TYPE_SWITCH,
10943     &setup.editor.el_custom,                    "editor.el_custom"
10944   },
10945   {
10946     TYPE_SWITCH,
10947     &setup.editor.el_user_defined,              "editor.el_user_defined"
10948   },
10949   {
10950     TYPE_SWITCH,
10951     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10952   },
10953   {
10954     TYPE_SWITCH,
10955     &setup.editor.el_headlines,                 "editor.el_headlines"
10956   },
10957   {
10958     TYPE_SWITCH,
10959     &setup.editor.show_element_token,           "editor.show_element_token"
10960   },
10961   {
10962     TYPE_SWITCH,
10963     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10964   },
10965 };
10966
10967 static struct TokenInfo editor_cascade_setup_tokens[] =
10968 {
10969   {
10970     TYPE_SWITCH,
10971     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10972   },
10973   {
10974     TYPE_SWITCH,
10975     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10976   },
10977   {
10978     TYPE_SWITCH,
10979     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10980   },
10981   {
10982     TYPE_SWITCH,
10983     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10984   },
10985   {
10986     TYPE_SWITCH,
10987     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10988   },
10989   {
10990     TYPE_SWITCH,
10991     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10992   },
10993   {
10994     TYPE_SWITCH,
10995     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10996   },
10997   {
10998     TYPE_SWITCH,
10999     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
11000   },
11001   {
11002     TYPE_SWITCH,
11003     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
11004   },
11005   {
11006     TYPE_SWITCH,
11007     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11008   },
11009   {
11010     TYPE_SWITCH,
11011     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11012   },
11013   {
11014     TYPE_SWITCH,
11015     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11016   },
11017   {
11018     TYPE_SWITCH,
11019     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11020   },
11021   {
11022     TYPE_SWITCH,
11023     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11024   },
11025   {
11026     TYPE_SWITCH,
11027     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11028   },
11029   {
11030     TYPE_SWITCH,
11031     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11032   },
11033   {
11034     TYPE_SWITCH,
11035     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11036   },
11037   {
11038     TYPE_SWITCH,
11039     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11040   },
11041   {
11042     TYPE_SWITCH,
11043     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11044   },
11045   {
11046     TYPE_SWITCH,
11047     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11048   },
11049 };
11050
11051 static struct TokenInfo shortcut_setup_tokens[] =
11052 {
11053   {
11054     TYPE_KEY_X11,
11055     &setup.shortcut.save_game,                  "shortcut.save_game"
11056   },
11057   {
11058     TYPE_KEY_X11,
11059     &setup.shortcut.load_game,                  "shortcut.load_game"
11060   },
11061   {
11062     TYPE_KEY_X11,
11063     &setup.shortcut.restart_game,               "shortcut.restart_game"
11064   },
11065   {
11066     TYPE_KEY_X11,
11067     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11068   },
11069   {
11070     TYPE_KEY_X11,
11071     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11072   },
11073   {
11074     TYPE_KEY_X11,
11075     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11076   },
11077   {
11078     TYPE_KEY_X11,
11079     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11080   },
11081   {
11082     TYPE_KEY_X11,
11083     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11084   },
11085   {
11086     TYPE_KEY_X11,
11087     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11088   },
11089   {
11090     TYPE_KEY_X11,
11091     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11092   },
11093   {
11094     TYPE_KEY_X11,
11095     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11096   },
11097   {
11098     TYPE_KEY_X11,
11099     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11100   },
11101   {
11102     TYPE_KEY_X11,
11103     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11104   },
11105   {
11106     TYPE_KEY_X11,
11107     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11108   },
11109   {
11110     TYPE_KEY_X11,
11111     &setup.shortcut.tape_record,                "shortcut.tape_record"
11112   },
11113   {
11114     TYPE_KEY_X11,
11115     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11116   },
11117   {
11118     TYPE_KEY_X11,
11119     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11120   },
11121   {
11122     TYPE_KEY_X11,
11123     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11124   },
11125   {
11126     TYPE_KEY_X11,
11127     &setup.shortcut.sound_music,                "shortcut.sound_music"
11128   },
11129   {
11130     TYPE_KEY_X11,
11131     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11132   },
11133   {
11134     TYPE_KEY_X11,
11135     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11136   },
11137   {
11138     TYPE_KEY_X11,
11139     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11140   },
11141   {
11142     TYPE_KEY_X11,
11143     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11144   },
11145 };
11146
11147 static struct SetupInputInfo setup_input;
11148 static struct TokenInfo player_setup_tokens[] =
11149 {
11150   {
11151     TYPE_BOOLEAN,
11152     &setup_input.use_joystick,                  ".use_joystick"
11153   },
11154   {
11155     TYPE_STRING,
11156     &setup_input.joy.device_name,               ".joy.device_name"
11157   },
11158   {
11159     TYPE_INTEGER,
11160     &setup_input.joy.xleft,                     ".joy.xleft"
11161   },
11162   {
11163     TYPE_INTEGER,
11164     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11165   },
11166   {
11167     TYPE_INTEGER,
11168     &setup_input.joy.xright,                    ".joy.xright"
11169   },
11170   {
11171     TYPE_INTEGER,
11172     &setup_input.joy.yupper,                    ".joy.yupper"
11173   },
11174   {
11175     TYPE_INTEGER,
11176     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11177   },
11178   {
11179     TYPE_INTEGER,
11180     &setup_input.joy.ylower,                    ".joy.ylower"
11181   },
11182   {
11183     TYPE_INTEGER,
11184     &setup_input.joy.snap,                      ".joy.snap_field"
11185   },
11186   {
11187     TYPE_INTEGER,
11188     &setup_input.joy.drop,                      ".joy.place_bomb"
11189   },
11190   {
11191     TYPE_KEY_X11,
11192     &setup_input.key.left,                      ".key.move_left"
11193   },
11194   {
11195     TYPE_KEY_X11,
11196     &setup_input.key.right,                     ".key.move_right"
11197   },
11198   {
11199     TYPE_KEY_X11,
11200     &setup_input.key.up,                        ".key.move_up"
11201   },
11202   {
11203     TYPE_KEY_X11,
11204     &setup_input.key.down,                      ".key.move_down"
11205   },
11206   {
11207     TYPE_KEY_X11,
11208     &setup_input.key.snap,                      ".key.snap_field"
11209   },
11210   {
11211     TYPE_KEY_X11,
11212     &setup_input.key.drop,                      ".key.place_bomb"
11213   },
11214 };
11215
11216 static struct TokenInfo system_setup_tokens[] =
11217 {
11218   {
11219     TYPE_STRING,
11220     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11221   },
11222   {
11223     TYPE_STRING,
11224     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11225   },
11226   {
11227     TYPE_STRING,
11228     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11229   },
11230   {
11231     TYPE_INTEGER,
11232     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11233   },
11234 };
11235
11236 static struct TokenInfo internal_setup_tokens[] =
11237 {
11238   {
11239     TYPE_STRING,
11240     &setup.internal.program_title,              "program_title"
11241   },
11242   {
11243     TYPE_STRING,
11244     &setup.internal.program_version,            "program_version"
11245   },
11246   {
11247     TYPE_STRING,
11248     &setup.internal.program_author,             "program_author"
11249   },
11250   {
11251     TYPE_STRING,
11252     &setup.internal.program_email,              "program_email"
11253   },
11254   {
11255     TYPE_STRING,
11256     &setup.internal.program_website,            "program_website"
11257   },
11258   {
11259     TYPE_STRING,
11260     &setup.internal.program_copyright,          "program_copyright"
11261   },
11262   {
11263     TYPE_STRING,
11264     &setup.internal.program_company,            "program_company"
11265   },
11266   {
11267     TYPE_STRING,
11268     &setup.internal.program_icon_file,          "program_icon_file"
11269   },
11270   {
11271     TYPE_STRING,
11272     &setup.internal.default_graphics_set,       "default_graphics_set"
11273   },
11274   {
11275     TYPE_STRING,
11276     &setup.internal.default_sounds_set,         "default_sounds_set"
11277   },
11278   {
11279     TYPE_STRING,
11280     &setup.internal.default_music_set,          "default_music_set"
11281   },
11282   {
11283     TYPE_STRING,
11284     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11285   },
11286   {
11287     TYPE_STRING,
11288     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11289   },
11290   {
11291     TYPE_STRING,
11292     &setup.internal.fallback_music_file,        "fallback_music_file"
11293   },
11294   {
11295     TYPE_STRING,
11296     &setup.internal.default_level_series,       "default_level_series"
11297   },
11298   {
11299     TYPE_INTEGER,
11300     &setup.internal.default_window_width,       "default_window_width"
11301   },
11302   {
11303     TYPE_INTEGER,
11304     &setup.internal.default_window_height,      "default_window_height"
11305   },
11306   {
11307     TYPE_BOOLEAN,
11308     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11309   },
11310   {
11311     TYPE_BOOLEAN,
11312     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11313   },
11314   {
11315     TYPE_BOOLEAN,
11316     &setup.internal.create_user_levelset,       "create_user_levelset"
11317   },
11318   {
11319     TYPE_BOOLEAN,
11320     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11321   },
11322   {
11323     TYPE_BOOLEAN,
11324     &setup.internal.menu_game,                  "menu_game"
11325   },
11326   {
11327     TYPE_BOOLEAN,
11328     &setup.internal.menu_engines,               "menu_engines"
11329   },
11330   {
11331     TYPE_BOOLEAN,
11332     &setup.internal.menu_editor,                "menu_editor"
11333   },
11334   {
11335     TYPE_BOOLEAN,
11336     &setup.internal.menu_graphics,              "menu_graphics"
11337   },
11338   {
11339     TYPE_BOOLEAN,
11340     &setup.internal.menu_sound,                 "menu_sound"
11341   },
11342   {
11343     TYPE_BOOLEAN,
11344     &setup.internal.menu_artwork,               "menu_artwork"
11345   },
11346   {
11347     TYPE_BOOLEAN,
11348     &setup.internal.menu_input,                 "menu_input"
11349   },
11350   {
11351     TYPE_BOOLEAN,
11352     &setup.internal.menu_touch,                 "menu_touch"
11353   },
11354   {
11355     TYPE_BOOLEAN,
11356     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11357   },
11358   {
11359     TYPE_BOOLEAN,
11360     &setup.internal.menu_exit,                  "menu_exit"
11361   },
11362   {
11363     TYPE_BOOLEAN,
11364     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11365   },
11366   {
11367     TYPE_BOOLEAN,
11368     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11369   },
11370   {
11371     TYPE_BOOLEAN,
11372     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11373   },
11374   {
11375     TYPE_BOOLEAN,
11376     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11377   },
11378   {
11379     TYPE_BOOLEAN,
11380     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11381   },
11382   {
11383     TYPE_BOOLEAN,
11384     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11385   },
11386   {
11387     TYPE_BOOLEAN,
11388     &setup.internal.info_title,                 "info_title"
11389   },
11390   {
11391     TYPE_BOOLEAN,
11392     &setup.internal.info_elements,              "info_elements"
11393   },
11394   {
11395     TYPE_BOOLEAN,
11396     &setup.internal.info_music,                 "info_music"
11397   },
11398   {
11399     TYPE_BOOLEAN,
11400     &setup.internal.info_credits,               "info_credits"
11401   },
11402   {
11403     TYPE_BOOLEAN,
11404     &setup.internal.info_program,               "info_program"
11405   },
11406   {
11407     TYPE_BOOLEAN,
11408     &setup.internal.info_version,               "info_version"
11409   },
11410   {
11411     TYPE_BOOLEAN,
11412     &setup.internal.info_levelset,              "info_levelset"
11413   },
11414   {
11415     TYPE_BOOLEAN,
11416     &setup.internal.info_exit,                  "info_exit"
11417   },
11418 };
11419
11420 static struct TokenInfo debug_setup_tokens[] =
11421 {
11422   {
11423     TYPE_INTEGER,
11424     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11425   },
11426   {
11427     TYPE_INTEGER,
11428     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11429   },
11430   {
11431     TYPE_INTEGER,
11432     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11433   },
11434   {
11435     TYPE_INTEGER,
11436     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11437   },
11438   {
11439     TYPE_INTEGER,
11440     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11441   },
11442   {
11443     TYPE_INTEGER,
11444     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11445   },
11446   {
11447     TYPE_INTEGER,
11448     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11449   },
11450   {
11451     TYPE_INTEGER,
11452     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11453   },
11454   {
11455     TYPE_INTEGER,
11456     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11457   },
11458   {
11459     TYPE_INTEGER,
11460     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11461   },
11462   {
11463     TYPE_KEY_X11,
11464     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11465   },
11466   {
11467     TYPE_KEY_X11,
11468     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11469   },
11470   {
11471     TYPE_KEY_X11,
11472     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11473   },
11474   {
11475     TYPE_KEY_X11,
11476     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11477   },
11478   {
11479     TYPE_KEY_X11,
11480     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11481   },
11482   {
11483     TYPE_KEY_X11,
11484     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11485   },
11486   {
11487     TYPE_KEY_X11,
11488     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11489   },
11490   {
11491     TYPE_KEY_X11,
11492     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11493   },
11494   {
11495     TYPE_KEY_X11,
11496     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11497   },
11498   {
11499     TYPE_KEY_X11,
11500     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11501   },
11502   {
11503     TYPE_BOOLEAN,
11504     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11505   {
11506     TYPE_BOOLEAN,
11507     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11508   },
11509   {
11510     TYPE_BOOLEAN,
11511     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11512   },
11513   {
11514     TYPE_SWITCH3,
11515     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11516   },
11517   {
11518     TYPE_INTEGER,
11519     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11520   },
11521 };
11522
11523 static struct TokenInfo options_setup_tokens[] =
11524 {
11525   {
11526     TYPE_BOOLEAN,
11527     &setup.options.verbose,                     "options.verbose"
11528   },
11529   {
11530     TYPE_BOOLEAN,
11531     &setup.options.debug,                       "options.debug"
11532   },
11533   {
11534     TYPE_STRING,
11535     &setup.options.debug_mode,                  "options.debug_mode"
11536   },
11537 };
11538
11539 static void setSetupInfoToDefaults(struct SetupInfo *si)
11540 {
11541   int i;
11542
11543   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11544
11545   si->multiple_users = TRUE;
11546
11547   si->sound = TRUE;
11548   si->sound_loops = TRUE;
11549   si->sound_music = TRUE;
11550   si->sound_simple = TRUE;
11551   si->toons = TRUE;
11552   si->global_animations = TRUE;
11553   si->scroll_delay = TRUE;
11554   si->forced_scroll_delay = FALSE;
11555   si->scroll_delay_value = STD_SCROLL_DELAY;
11556   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11557   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11558   si->fade_screens = TRUE;
11559   si->autorecord = TRUE;
11560   si->autorecord_after_replay = TRUE;
11561   si->auto_pause_on_start = FALSE;
11562   si->show_titlescreen = TRUE;
11563   si->quick_doors = FALSE;
11564   si->team_mode = FALSE;
11565   si->handicap = TRUE;
11566   si->skip_levels = TRUE;
11567   si->increment_levels = TRUE;
11568   si->auto_play_next_level = TRUE;
11569   si->count_score_after_game = TRUE;
11570   si->show_scores_after_game = TRUE;
11571   si->time_limit = TRUE;
11572   si->fullscreen = FALSE;
11573   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11574   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11575   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11576   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11577   si->ask_on_escape = TRUE;
11578   si->ask_on_escape_editor = TRUE;
11579   si->ask_on_game_over = TRUE;
11580   si->ask_on_quit_game = TRUE;
11581   si->ask_on_quit_program = TRUE;
11582   si->quick_switch = FALSE;
11583   si->input_on_focus = FALSE;
11584   si->prefer_aga_graphics = TRUE;
11585   si->prefer_lowpass_sounds = FALSE;
11586   si->prefer_extra_panel_items = TRUE;
11587   si->game_speed_extended = FALSE;
11588   si->game_frame_delay = GAME_FRAME_DELAY;
11589   si->default_game_engine_type  = GAME_ENGINE_TYPE_RND;
11590   si->bd_skip_uncovering = FALSE;
11591   si->bd_skip_hatching = FALSE;
11592   si->bd_scroll_delay = TRUE;
11593   si->bd_smooth_movements = AUTO;
11594   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11595   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11596   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11597   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11598   si->sp_show_border_elements = FALSE;
11599   si->small_game_graphics = FALSE;
11600   si->show_load_save_buttons = FALSE;
11601   si->show_undo_redo_buttons = FALSE;
11602   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11603
11604   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11605   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11606   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11607
11608   si->override_level_graphics = FALSE;
11609   si->override_level_sounds = FALSE;
11610   si->override_level_music = FALSE;
11611
11612   si->volume_simple = 100;              // percent
11613   si->volume_loops = 100;               // percent
11614   si->volume_music = 100;               // percent
11615
11616   si->network_mode = FALSE;
11617   si->network_player_nr = 0;            // first player
11618   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11619
11620   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11621   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11622   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11623   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11624   si->touch.draw_outlined = TRUE;
11625   si->touch.draw_pressed = TRUE;
11626
11627   for (i = 0; i < 2; i++)
11628   {
11629     char *default_grid_button[6][2] =
11630     {
11631       { "      ", "  ^^  " },
11632       { "      ", "  ^^  " },
11633       { "      ", "<<  >>" },
11634       { "      ", "<<  >>" },
11635       { "111222", "  vv  " },
11636       { "111222", "  vv  " }
11637     };
11638     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11639     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11640     int min_xsize = MIN(6, grid_xsize);
11641     int min_ysize = MIN(6, grid_ysize);
11642     int startx = grid_xsize - min_xsize;
11643     int starty = grid_ysize - min_ysize;
11644     int x, y;
11645
11646     // virtual buttons grid can only be set to defaults if video is initialized
11647     // (this will be repeated if virtual buttons are not loaded from setup file)
11648     if (video.initialized)
11649     {
11650       si->touch.grid_xsize[i] = grid_xsize;
11651       si->touch.grid_ysize[i] = grid_ysize;
11652     }
11653     else
11654     {
11655       si->touch.grid_xsize[i] = -1;
11656       si->touch.grid_ysize[i] = -1;
11657     }
11658
11659     for (x = 0; x < MAX_GRID_XSIZE; x++)
11660       for (y = 0; y < MAX_GRID_YSIZE; y++)
11661         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11662
11663     for (x = 0; x < min_xsize; x++)
11664       for (y = 0; y < min_ysize; y++)
11665         si->touch.grid_button[i][x][starty + y] =
11666           default_grid_button[y][0][x];
11667
11668     for (x = 0; x < min_xsize; x++)
11669       for (y = 0; y < min_ysize; y++)
11670         si->touch.grid_button[i][startx + x][starty + y] =
11671           default_grid_button[y][1][x];
11672   }
11673
11674   si->touch.grid_initialized            = video.initialized;
11675
11676   si->touch.overlay_buttons             = FALSE;
11677
11678   si->editor.el_boulderdash             = TRUE;
11679   si->editor.el_boulderdash_native      = TRUE;
11680   si->editor.el_boulderdash_effects     = TRUE;
11681   si->editor.el_emerald_mine            = TRUE;
11682   si->editor.el_emerald_mine_club       = TRUE;
11683   si->editor.el_more                    = TRUE;
11684   si->editor.el_sokoban                 = TRUE;
11685   si->editor.el_supaplex                = TRUE;
11686   si->editor.el_diamond_caves           = TRUE;
11687   si->editor.el_dx_boulderdash          = TRUE;
11688
11689   si->editor.el_mirror_magic            = TRUE;
11690   si->editor.el_deflektor               = TRUE;
11691
11692   si->editor.el_chars                   = TRUE;
11693   si->editor.el_steel_chars             = TRUE;
11694
11695   si->editor.el_classic                 = TRUE;
11696   si->editor.el_custom                  = TRUE;
11697
11698   si->editor.el_user_defined            = FALSE;
11699   si->editor.el_dynamic                 = TRUE;
11700
11701   si->editor.el_headlines               = TRUE;
11702
11703   si->editor.show_element_token         = FALSE;
11704
11705   si->editor.show_read_only_warning     = TRUE;
11706
11707   si->editor.use_template_for_new_levels = TRUE;
11708
11709   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11710   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11711   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11712   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11713   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11714
11715   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11716   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11717   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11718   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11719   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11720
11721   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11722   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11723   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11724   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11725   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11726   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11727
11728   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11729   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11730   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11731
11732   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11733   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11734   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11735   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11736
11737   for (i = 0; i < MAX_PLAYERS; i++)
11738   {
11739     si->input[i].use_joystick = FALSE;
11740     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11741     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11742     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11743     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11744     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11745     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11746     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11747     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11748     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11749     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11750     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11751     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11752     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11753     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11754     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11755   }
11756
11757   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11758   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11759   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11760   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11761
11762   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11763   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11764   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11765   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11766   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11767   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11768   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11769
11770   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11771
11772   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11773   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11774   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11775
11776   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11777   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11778   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11779
11780   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11781   si->internal.choose_from_top_leveldir = FALSE;
11782   si->internal.show_scaling_in_title = TRUE;
11783   si->internal.create_user_levelset = TRUE;
11784   si->internal.info_screens_from_main = FALSE;
11785
11786   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11787   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11788
11789   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11790   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11791   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11792   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11793   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11794   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11795   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11796   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11797   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11798   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11799
11800   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11801   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11802   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11803   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11804   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11805   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11806   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11807   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11808   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11809   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11810
11811   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11812   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11813
11814   si->debug.show_frames_per_second = FALSE;
11815
11816   si->debug.xsn_mode = AUTO;
11817   si->debug.xsn_percent = 0;
11818
11819   si->options.verbose = FALSE;
11820   si->options.debug = FALSE;
11821   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11822
11823 #if defined(PLATFORM_ANDROID)
11824   si->fullscreen = TRUE;
11825   si->touch.overlay_buttons = TRUE;
11826 #endif
11827
11828   setHideSetupEntry(&setup.debug.xsn_mode);
11829 }
11830
11831 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11832 {
11833   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11834 }
11835
11836 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11837 {
11838   si->player_uuid = NULL;       // (will be set later)
11839   si->player_version = 1;       // (will be set later)
11840
11841   si->use_api_server = TRUE;
11842   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11843   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11844   si->ask_for_uploading_tapes = TRUE;
11845   si->ask_for_remaining_tapes = FALSE;
11846   si->provide_uploading_tapes = TRUE;
11847   si->ask_for_using_api_server = TRUE;
11848   si->has_remaining_tapes = FALSE;
11849 }
11850
11851 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11852 {
11853   si->editor_cascade.el_bd              = TRUE;
11854   si->editor_cascade.el_bd_native       = TRUE;
11855   si->editor_cascade.el_bd_effects      = FALSE;
11856   si->editor_cascade.el_em              = TRUE;
11857   si->editor_cascade.el_emc             = TRUE;
11858   si->editor_cascade.el_rnd             = TRUE;
11859   si->editor_cascade.el_sb              = TRUE;
11860   si->editor_cascade.el_sp              = TRUE;
11861   si->editor_cascade.el_dc              = TRUE;
11862   si->editor_cascade.el_dx              = TRUE;
11863
11864   si->editor_cascade.el_mm              = TRUE;
11865   si->editor_cascade.el_df              = TRUE;
11866
11867   si->editor_cascade.el_chars           = FALSE;
11868   si->editor_cascade.el_steel_chars     = FALSE;
11869   si->editor_cascade.el_ce              = FALSE;
11870   si->editor_cascade.el_ge              = FALSE;
11871   si->editor_cascade.el_es              = FALSE;
11872   si->editor_cascade.el_ref             = FALSE;
11873   si->editor_cascade.el_user            = FALSE;
11874   si->editor_cascade.el_dynamic         = FALSE;
11875 }
11876
11877 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11878
11879 static char *getHideSetupToken(void *setup_value)
11880 {
11881   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11882
11883   if (setup_value != NULL)
11884     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11885
11886   return hide_setup_token;
11887 }
11888
11889 void setHideSetupEntry(void *setup_value)
11890 {
11891   char *hide_setup_token = getHideSetupToken(setup_value);
11892
11893   if (hide_setup_hash == NULL)
11894     hide_setup_hash = newSetupFileHash();
11895
11896   if (setup_value != NULL)
11897     setHashEntry(hide_setup_hash, hide_setup_token, "");
11898 }
11899
11900 void removeHideSetupEntry(void *setup_value)
11901 {
11902   char *hide_setup_token = getHideSetupToken(setup_value);
11903
11904   if (setup_value != NULL)
11905     removeHashEntry(hide_setup_hash, hide_setup_token);
11906 }
11907
11908 boolean hideSetupEntry(void *setup_value)
11909 {
11910   char *hide_setup_token = getHideSetupToken(setup_value);
11911
11912   return (setup_value != NULL &&
11913           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11914 }
11915
11916 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11917                                       struct TokenInfo *token_info,
11918                                       int token_nr, char *token_text)
11919 {
11920   char *token_hide_text = getStringCat2(token_text, ".hide");
11921   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11922
11923   // set the value of this setup option in the setup option structure
11924   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11925
11926   // check if this setup option should be hidden in the setup menu
11927   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11928     setHideSetupEntry(token_info[token_nr].value);
11929
11930   free(token_hide_text);
11931 }
11932
11933 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11934                                       struct TokenInfo *token_info,
11935                                       int token_nr)
11936 {
11937   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11938                             token_info[token_nr].text);
11939 }
11940
11941 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11942 {
11943   int i, pnr;
11944
11945   if (!setup_file_hash)
11946     return;
11947
11948   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11949     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11950
11951   setup.touch.grid_initialized = TRUE;
11952   for (i = 0; i < 2; i++)
11953   {
11954     int grid_xsize = setup.touch.grid_xsize[i];
11955     int grid_ysize = setup.touch.grid_ysize[i];
11956     int x, y;
11957
11958     // if virtual buttons are not loaded from setup file, repeat initializing
11959     // virtual buttons grid with default values later when video is initialized
11960     if (grid_xsize == -1 ||
11961         grid_ysize == -1)
11962     {
11963       setup.touch.grid_initialized = FALSE;
11964
11965       continue;
11966     }
11967
11968     for (y = 0; y < grid_ysize; y++)
11969     {
11970       char token_string[MAX_LINE_LEN];
11971
11972       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11973
11974       char *value_string = getHashEntry(setup_file_hash, token_string);
11975
11976       if (value_string == NULL)
11977         continue;
11978
11979       for (x = 0; x < grid_xsize; x++)
11980       {
11981         char c = value_string[x];
11982
11983         setup.touch.grid_button[i][x][y] =
11984           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11985       }
11986     }
11987   }
11988
11989   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11990     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11991
11992   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11993     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11994
11995   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11996   {
11997     char prefix[30];
11998
11999     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12000
12001     setup_input = setup.input[pnr];
12002     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12003     {
12004       char full_token[100];
12005
12006       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
12007       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12008                                 full_token);
12009     }
12010     setup.input[pnr] = setup_input;
12011   }
12012
12013   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12014     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12015
12016   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12017     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12018
12019   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12020     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12021
12022   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12023     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12024
12025   setHideRelatedSetupEntries();
12026 }
12027
12028 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12029 {
12030   int i;
12031
12032   if (!setup_file_hash)
12033     return;
12034
12035   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12036     setSetupInfo(auto_setup_tokens, i,
12037                  getHashEntry(setup_file_hash,
12038                               auto_setup_tokens[i].text));
12039 }
12040
12041 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12042 {
12043   int i;
12044
12045   if (!setup_file_hash)
12046     return;
12047
12048   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12049     setSetupInfo(server_setup_tokens, i,
12050                  getHashEntry(setup_file_hash,
12051                               server_setup_tokens[i].text));
12052 }
12053
12054 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12055 {
12056   int i;
12057
12058   if (!setup_file_hash)
12059     return;
12060
12061   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12062     setSetupInfo(editor_cascade_setup_tokens, i,
12063                  getHashEntry(setup_file_hash,
12064                               editor_cascade_setup_tokens[i].text));
12065 }
12066
12067 void LoadUserNames(void)
12068 {
12069   int last_user_nr = user.nr;
12070   int i;
12071
12072   if (global.user_names != NULL)
12073   {
12074     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12075       checked_free(global.user_names[i]);
12076
12077     checked_free(global.user_names);
12078   }
12079
12080   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12081
12082   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12083   {
12084     user.nr = i;
12085
12086     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12087
12088     if (setup_file_hash)
12089     {
12090       char *player_name = getHashEntry(setup_file_hash, "player_name");
12091
12092       global.user_names[i] = getFixedUserName(player_name);
12093
12094       freeSetupFileHash(setup_file_hash);
12095     }
12096
12097     if (global.user_names[i] == NULL)
12098       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12099   }
12100
12101   user.nr = last_user_nr;
12102 }
12103
12104 void LoadSetupFromFilename(char *filename)
12105 {
12106   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12107
12108   if (setup_file_hash)
12109   {
12110     decodeSetupFileHash_Default(setup_file_hash);
12111
12112     freeSetupFileHash(setup_file_hash);
12113   }
12114   else
12115   {
12116     Debug("setup", "using default setup values");
12117   }
12118 }
12119
12120 static void LoadSetup_SpecialPostProcessing(void)
12121 {
12122   char *player_name_new;
12123
12124   // needed to work around problems with fixed length strings
12125   player_name_new = getFixedUserName(setup.player_name);
12126   free(setup.player_name);
12127   setup.player_name = player_name_new;
12128
12129   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12130   if (setup.scroll_delay == FALSE)
12131   {
12132     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12133     setup.scroll_delay = TRUE;                  // now always "on"
12134   }
12135
12136   // make sure that scroll delay value stays inside valid range
12137   setup.scroll_delay_value =
12138     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12139 }
12140
12141 void LoadSetup_Default(void)
12142 {
12143   char *filename;
12144
12145   // always start with reliable default values
12146   setSetupInfoToDefaults(&setup);
12147
12148   // try to load setup values from default setup file
12149   filename = getDefaultSetupFilename();
12150
12151   if (fileExists(filename))
12152     LoadSetupFromFilename(filename);
12153
12154   // try to load setup values from platform setup file
12155   filename = getPlatformSetupFilename();
12156
12157   if (fileExists(filename))
12158     LoadSetupFromFilename(filename);
12159
12160   // try to load setup values from user setup file
12161   filename = getSetupFilename();
12162
12163   LoadSetupFromFilename(filename);
12164
12165   LoadSetup_SpecialPostProcessing();
12166 }
12167
12168 void LoadSetup_AutoSetup(void)
12169 {
12170   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12171   SetupFileHash *setup_file_hash = NULL;
12172
12173   // always start with reliable default values
12174   setSetupInfoToDefaults_AutoSetup(&setup);
12175
12176   setup_file_hash = loadSetupFileHash(filename);
12177
12178   if (setup_file_hash)
12179   {
12180     decodeSetupFileHash_AutoSetup(setup_file_hash);
12181
12182     freeSetupFileHash(setup_file_hash);
12183   }
12184
12185   free(filename);
12186 }
12187
12188 void LoadSetup_ServerSetup(void)
12189 {
12190   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12191   SetupFileHash *setup_file_hash = NULL;
12192
12193   // always start with reliable default values
12194   setSetupInfoToDefaults_ServerSetup(&setup);
12195
12196   setup_file_hash = loadSetupFileHash(filename);
12197
12198   if (setup_file_hash)
12199   {
12200     decodeSetupFileHash_ServerSetup(setup_file_hash);
12201
12202     freeSetupFileHash(setup_file_hash);
12203   }
12204
12205   free(filename);
12206
12207   if (setup.player_uuid == NULL)
12208   {
12209     // player UUID does not yet exist in setup file
12210     setup.player_uuid = getStringCopy(getUUID());
12211     setup.player_version = 2;
12212
12213     SaveSetup_ServerSetup();
12214   }
12215 }
12216
12217 void LoadSetup_EditorCascade(void)
12218 {
12219   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12220   SetupFileHash *setup_file_hash = NULL;
12221
12222   // always start with reliable default values
12223   setSetupInfoToDefaults_EditorCascade(&setup);
12224
12225   setup_file_hash = loadSetupFileHash(filename);
12226
12227   if (setup_file_hash)
12228   {
12229     decodeSetupFileHash_EditorCascade(setup_file_hash);
12230
12231     freeSetupFileHash(setup_file_hash);
12232   }
12233
12234   free(filename);
12235 }
12236
12237 void LoadSetup(void)
12238 {
12239   LoadSetup_Default();
12240   LoadSetup_AutoSetup();
12241   LoadSetup_ServerSetup();
12242   LoadSetup_EditorCascade();
12243 }
12244
12245 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12246                                            char *mapping_line)
12247 {
12248   char mapping_guid[MAX_LINE_LEN];
12249   char *mapping_start, *mapping_end;
12250
12251   // get GUID from game controller mapping line: copy complete line
12252   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12253   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12254
12255   // get GUID from game controller mapping line: cut after GUID part
12256   mapping_start = strchr(mapping_guid, ',');
12257   if (mapping_start != NULL)
12258     *mapping_start = '\0';
12259
12260   // cut newline from game controller mapping line
12261   mapping_end = strchr(mapping_line, '\n');
12262   if (mapping_end != NULL)
12263     *mapping_end = '\0';
12264
12265   // add mapping entry to game controller mappings hash
12266   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12267 }
12268
12269 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12270                                                  char *filename)
12271 {
12272   FILE *file;
12273
12274   if (!(file = fopen(filename, MODE_READ)))
12275   {
12276     Warn("cannot read game controller mappings file '%s'", filename);
12277
12278     return;
12279   }
12280
12281   while (!feof(file))
12282   {
12283     char line[MAX_LINE_LEN];
12284
12285     if (!fgets(line, MAX_LINE_LEN, file))
12286       break;
12287
12288     addGameControllerMappingToHash(mappings_hash, line);
12289   }
12290
12291   fclose(file);
12292 }
12293
12294 void SaveSetup_Default(void)
12295 {
12296   char *filename = getSetupFilename();
12297   FILE *file;
12298   int i, pnr;
12299
12300   InitUserDataDirectory();
12301
12302   if (!(file = fopen(filename, MODE_WRITE)))
12303   {
12304     Warn("cannot write setup file '%s'", filename);
12305
12306     return;
12307   }
12308
12309   fprintFileHeader(file, SETUP_FILENAME);
12310
12311   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12312   {
12313     // just to make things nicer :)
12314     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12315         global_setup_tokens[i].value == &setup.sound                    ||
12316         global_setup_tokens[i].value == &setup.graphics_set             ||
12317         global_setup_tokens[i].value == &setup.volume_simple            ||
12318         global_setup_tokens[i].value == &setup.network_mode             ||
12319         global_setup_tokens[i].value == &setup.touch.control_type       ||
12320         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12321         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12322       fprintf(file, "\n");
12323
12324     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12325   }
12326
12327   for (i = 0; i < 2; i++)
12328   {
12329     int grid_xsize = setup.touch.grid_xsize[i];
12330     int grid_ysize = setup.touch.grid_ysize[i];
12331     int x, y;
12332
12333     fprintf(file, "\n");
12334
12335     for (y = 0; y < grid_ysize; y++)
12336     {
12337       char token_string[MAX_LINE_LEN];
12338       char value_string[MAX_LINE_LEN];
12339
12340       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12341
12342       for (x = 0; x < grid_xsize; x++)
12343       {
12344         char c = setup.touch.grid_button[i][x][y];
12345
12346         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12347       }
12348
12349       value_string[grid_xsize] = '\0';
12350
12351       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12352     }
12353   }
12354
12355   fprintf(file, "\n");
12356   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12357     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12358
12359   fprintf(file, "\n");
12360   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12361     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12362
12363   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12364   {
12365     char prefix[30];
12366
12367     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12368     fprintf(file, "\n");
12369
12370     setup_input = setup.input[pnr];
12371     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12372       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12373   }
12374
12375   fprintf(file, "\n");
12376   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12377     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12378
12379   // (internal setup values not saved to user setup file)
12380
12381   fprintf(file, "\n");
12382   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12383     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12384         setup.debug.xsn_mode != AUTO)
12385       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12386
12387   fprintf(file, "\n");
12388   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12389     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12390
12391   fclose(file);
12392
12393   SetFilePermissions(filename, PERMS_PRIVATE);
12394 }
12395
12396 void SaveSetup_AutoSetup(void)
12397 {
12398   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12399   FILE *file;
12400   int i;
12401
12402   InitUserDataDirectory();
12403
12404   if (!(file = fopen(filename, MODE_WRITE)))
12405   {
12406     Warn("cannot write auto setup file '%s'", filename);
12407
12408     free(filename);
12409
12410     return;
12411   }
12412
12413   fprintFileHeader(file, AUTOSETUP_FILENAME);
12414
12415   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12416     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12417
12418   fclose(file);
12419
12420   SetFilePermissions(filename, PERMS_PRIVATE);
12421
12422   free(filename);
12423 }
12424
12425 void SaveSetup_ServerSetup(void)
12426 {
12427   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12428   FILE *file;
12429   int i;
12430
12431   InitUserDataDirectory();
12432
12433   if (!(file = fopen(filename, MODE_WRITE)))
12434   {
12435     Warn("cannot write server setup file '%s'", filename);
12436
12437     free(filename);
12438
12439     return;
12440   }
12441
12442   fprintFileHeader(file, SERVERSETUP_FILENAME);
12443
12444   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12445   {
12446     // just to make things nicer :)
12447     if (server_setup_tokens[i].value == &setup.use_api_server)
12448       fprintf(file, "\n");
12449
12450     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12451   }
12452
12453   fclose(file);
12454
12455   SetFilePermissions(filename, PERMS_PRIVATE);
12456
12457   free(filename);
12458 }
12459
12460 void SaveSetup_EditorCascade(void)
12461 {
12462   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12463   FILE *file;
12464   int i;
12465
12466   InitUserDataDirectory();
12467
12468   if (!(file = fopen(filename, MODE_WRITE)))
12469   {
12470     Warn("cannot write editor cascade state file '%s'", filename);
12471
12472     free(filename);
12473
12474     return;
12475   }
12476
12477   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12478
12479   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12480     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12481
12482   fclose(file);
12483
12484   SetFilePermissions(filename, PERMS_PRIVATE);
12485
12486   free(filename);
12487 }
12488
12489 void SaveSetup(void)
12490 {
12491   SaveSetup_Default();
12492   SaveSetup_AutoSetup();
12493   SaveSetup_ServerSetup();
12494   SaveSetup_EditorCascade();
12495 }
12496
12497 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12498                                                   char *filename)
12499 {
12500   FILE *file;
12501
12502   if (!(file = fopen(filename, MODE_WRITE)))
12503   {
12504     Warn("cannot write game controller mappings file '%s'", filename);
12505
12506     return;
12507   }
12508
12509   BEGIN_HASH_ITERATION(mappings_hash, itr)
12510   {
12511     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12512   }
12513   END_HASH_ITERATION(mappings_hash, itr)
12514
12515   fclose(file);
12516 }
12517
12518 void SaveSetup_AddGameControllerMapping(char *mapping)
12519 {
12520   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12521   SetupFileHash *mappings_hash = newSetupFileHash();
12522
12523   InitUserDataDirectory();
12524
12525   // load existing personal game controller mappings
12526   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12527
12528   // add new mapping to personal game controller mappings
12529   addGameControllerMappingToHash(mappings_hash, mapping);
12530
12531   // save updated personal game controller mappings
12532   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12533
12534   freeSetupFileHash(mappings_hash);
12535   free(filename);
12536 }
12537
12538 void LoadCustomElementDescriptions(void)
12539 {
12540   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12541   SetupFileHash *setup_file_hash;
12542   int i;
12543
12544   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12545   {
12546     if (element_info[i].custom_description != NULL)
12547     {
12548       free(element_info[i].custom_description);
12549       element_info[i].custom_description = NULL;
12550     }
12551   }
12552
12553   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12554     return;
12555
12556   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12557   {
12558     char *token = getStringCat2(element_info[i].token_name, ".name");
12559     char *value = getHashEntry(setup_file_hash, token);
12560
12561     if (value != NULL)
12562       element_info[i].custom_description = getStringCopy(value);
12563
12564     free(token);
12565   }
12566
12567   freeSetupFileHash(setup_file_hash);
12568 }
12569
12570 static int getElementFromToken(char *token)
12571 {
12572   char *value = getHashEntry(element_token_hash, token);
12573
12574   if (value != NULL)
12575     return atoi(value);
12576
12577   Warn("unknown element token '%s'", token);
12578
12579   return EL_UNDEFINED;
12580 }
12581
12582 void FreeGlobalAnimEventInfo(void)
12583 {
12584   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12585
12586   if (gaei->event_list == NULL)
12587     return;
12588
12589   int i;
12590
12591   for (i = 0; i < gaei->num_event_lists; i++)
12592   {
12593     checked_free(gaei->event_list[i]->event_value);
12594     checked_free(gaei->event_list[i]);
12595   }
12596
12597   checked_free(gaei->event_list);
12598
12599   gaei->event_list = NULL;
12600   gaei->num_event_lists = 0;
12601 }
12602
12603 static int AddGlobalAnimEventList(void)
12604 {
12605   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12606   int list_pos = gaei->num_event_lists++;
12607
12608   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12609                                      sizeof(struct GlobalAnimEventListInfo *));
12610
12611   gaei->event_list[list_pos] =
12612     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12613
12614   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12615
12616   gaeli->event_value = NULL;
12617   gaeli->num_event_values = 0;
12618
12619   return list_pos;
12620 }
12621
12622 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12623 {
12624   // do not add empty global animation events
12625   if (event_value == ANIM_EVENT_NONE)
12626     return list_pos;
12627
12628   // if list position is undefined, create new list
12629   if (list_pos == ANIM_EVENT_UNDEFINED)
12630     list_pos = AddGlobalAnimEventList();
12631
12632   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12633   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12634   int value_pos = gaeli->num_event_values++;
12635
12636   gaeli->event_value = checked_realloc(gaeli->event_value,
12637                                        gaeli->num_event_values * sizeof(int *));
12638
12639   gaeli->event_value[value_pos] = event_value;
12640
12641   return list_pos;
12642 }
12643
12644 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12645 {
12646   if (list_pos == ANIM_EVENT_UNDEFINED)
12647     return ANIM_EVENT_NONE;
12648
12649   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12650   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12651
12652   return gaeli->event_value[value_pos];
12653 }
12654
12655 int GetGlobalAnimEventValueCount(int list_pos)
12656 {
12657   if (list_pos == ANIM_EVENT_UNDEFINED)
12658     return 0;
12659
12660   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12661   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12662
12663   return gaeli->num_event_values;
12664 }
12665
12666 // This function checks if a string <s> of the format "string1, string2, ..."
12667 // exactly contains a string <s_contained>.
12668
12669 static boolean string_has_parameter(char *s, char *s_contained)
12670 {
12671   char *substring;
12672
12673   if (s == NULL || s_contained == NULL)
12674     return FALSE;
12675
12676   if (strlen(s_contained) > strlen(s))
12677     return FALSE;
12678
12679   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12680   {
12681     char next_char = s[strlen(s_contained)];
12682
12683     // check if next character is delimiter or whitespace
12684     if (next_char == ',' || next_char == '\0' ||
12685         next_char == ' ' || next_char == '\t')
12686       return TRUE;
12687   }
12688
12689   // check if string contains another parameter string after a comma
12690   substring = strchr(s, ',');
12691   if (substring == NULL)        // string does not contain a comma
12692     return FALSE;
12693
12694   // advance string pointer to next character after the comma
12695   substring++;
12696
12697   // skip potential whitespaces after the comma
12698   while (*substring == ' ' || *substring == '\t')
12699     substring++;
12700
12701   return string_has_parameter(substring, s_contained);
12702 }
12703
12704 static int get_anim_parameter_value_ce(char *s)
12705 {
12706   char *s_ptr = s;
12707   char *pattern_1 = "ce_change:custom_";
12708   char *pattern_2 = ".page_";
12709   int pattern_1_len = strlen(pattern_1);
12710   char *matching_char = strstr(s_ptr, pattern_1);
12711   int result = ANIM_EVENT_NONE;
12712
12713   if (matching_char == NULL)
12714     return ANIM_EVENT_NONE;
12715
12716   result = ANIM_EVENT_CE_CHANGE;
12717
12718   s_ptr = matching_char + pattern_1_len;
12719
12720   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12721   if (*s_ptr >= '0' && *s_ptr <= '9')
12722   {
12723     int gic_ce_nr = (*s_ptr++ - '0');
12724
12725     if (*s_ptr >= '0' && *s_ptr <= '9')
12726     {
12727       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12728
12729       if (*s_ptr >= '0' && *s_ptr <= '9')
12730         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12731     }
12732
12733     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12734       return ANIM_EVENT_NONE;
12735
12736     // custom element stored as 0 to 255
12737     gic_ce_nr--;
12738
12739     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12740   }
12741   else
12742   {
12743     // invalid custom element number specified
12744
12745     return ANIM_EVENT_NONE;
12746   }
12747
12748   // check for change page number ("page_X" or "page_XX") (optional)
12749   if (strPrefix(s_ptr, pattern_2))
12750   {
12751     s_ptr += strlen(pattern_2);
12752
12753     if (*s_ptr >= '0' && *s_ptr <= '9')
12754     {
12755       int gic_page_nr = (*s_ptr++ - '0');
12756
12757       if (*s_ptr >= '0' && *s_ptr <= '9')
12758         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12759
12760       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12761         return ANIM_EVENT_NONE;
12762
12763       // change page stored as 1 to 32 (0 means "all change pages")
12764
12765       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12766     }
12767     else
12768     {
12769       // invalid animation part number specified
12770
12771       return ANIM_EVENT_NONE;
12772     }
12773   }
12774
12775   // discard result if next character is neither delimiter nor whitespace
12776   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12777         *s_ptr == ' ' || *s_ptr == '\t'))
12778     return ANIM_EVENT_NONE;
12779
12780   return result;
12781 }
12782
12783 static int get_anim_parameter_value(char *s)
12784 {
12785   int event_value[] =
12786   {
12787     ANIM_EVENT_CLICK,
12788     ANIM_EVENT_INIT,
12789     ANIM_EVENT_START,
12790     ANIM_EVENT_END,
12791     ANIM_EVENT_POST
12792   };
12793   char *pattern_1[] =
12794   {
12795     "click:anim_",
12796     "init:anim_",
12797     "start:anim_",
12798     "end:anim_",
12799     "post:anim_"
12800   };
12801   char *pattern_2 = ".part_";
12802   char *matching_char = NULL;
12803   char *s_ptr = s;
12804   int pattern_1_len = 0;
12805   int result = ANIM_EVENT_NONE;
12806   int i;
12807
12808   result = get_anim_parameter_value_ce(s);
12809
12810   if (result != ANIM_EVENT_NONE)
12811     return result;
12812
12813   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12814   {
12815     matching_char = strstr(s_ptr, pattern_1[i]);
12816     pattern_1_len = strlen(pattern_1[i]);
12817     result = event_value[i];
12818
12819     if (matching_char != NULL)
12820       break;
12821   }
12822
12823   if (matching_char == NULL)
12824     return ANIM_EVENT_NONE;
12825
12826   s_ptr = matching_char + pattern_1_len;
12827
12828   // check for main animation number ("anim_X" or "anim_XX")
12829   if (*s_ptr >= '0' && *s_ptr <= '9')
12830   {
12831     int gic_anim_nr = (*s_ptr++ - '0');
12832
12833     if (*s_ptr >= '0' && *s_ptr <= '9')
12834       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12835
12836     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12837       return ANIM_EVENT_NONE;
12838
12839     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12840   }
12841   else
12842   {
12843     // invalid main animation number specified
12844
12845     return ANIM_EVENT_NONE;
12846   }
12847
12848   // check for animation part number ("part_X" or "part_XX") (optional)
12849   if (strPrefix(s_ptr, pattern_2))
12850   {
12851     s_ptr += strlen(pattern_2);
12852
12853     if (*s_ptr >= '0' && *s_ptr <= '9')
12854     {
12855       int gic_part_nr = (*s_ptr++ - '0');
12856
12857       if (*s_ptr >= '0' && *s_ptr <= '9')
12858         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12859
12860       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12861         return ANIM_EVENT_NONE;
12862
12863       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12864     }
12865     else
12866     {
12867       // invalid animation part number specified
12868
12869       return ANIM_EVENT_NONE;
12870     }
12871   }
12872
12873   // discard result if next character is neither delimiter nor whitespace
12874   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12875         *s_ptr == ' ' || *s_ptr == '\t'))
12876     return ANIM_EVENT_NONE;
12877
12878   return result;
12879 }
12880
12881 static int get_anim_parameter_values(char *s)
12882 {
12883   int list_pos = ANIM_EVENT_UNDEFINED;
12884   int event_value = ANIM_EVENT_DEFAULT;
12885
12886   if (string_has_parameter(s, "any"))
12887     event_value |= ANIM_EVENT_ANY;
12888
12889   if (string_has_parameter(s, "click:self") ||
12890       string_has_parameter(s, "click") ||
12891       string_has_parameter(s, "self"))
12892     event_value |= ANIM_EVENT_SELF;
12893
12894   if (string_has_parameter(s, "unclick:any"))
12895     event_value |= ANIM_EVENT_UNCLICK_ANY;
12896
12897   // if animation event found, add it to global animation event list
12898   if (event_value != ANIM_EVENT_NONE)
12899     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12900
12901   while (s != NULL)
12902   {
12903     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12904     event_value = get_anim_parameter_value(s);
12905
12906     // if animation event found, add it to global animation event list
12907     if (event_value != ANIM_EVENT_NONE)
12908       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12909
12910     // continue with next part of the string, starting with next comma
12911     s = strchr(s + 1, ',');
12912   }
12913
12914   return list_pos;
12915 }
12916
12917 static int get_anim_action_parameter_value(char *token)
12918 {
12919   // check most common default case first to massively speed things up
12920   if (strEqual(token, ARG_UNDEFINED))
12921     return ANIM_EVENT_ACTION_NONE;
12922
12923   int result = getImageIDFromToken(token);
12924
12925   if (result == -1)
12926   {
12927     char *gfx_token = getStringCat2("gfx.", token);
12928
12929     result = getImageIDFromToken(gfx_token);
12930
12931     checked_free(gfx_token);
12932   }
12933
12934   if (result == -1)
12935   {
12936     Key key = getKeyFromX11KeyName(token);
12937
12938     if (key != KSYM_UNDEFINED)
12939       result = -(int)key;
12940   }
12941
12942   if (result == -1)
12943   {
12944     if (isURL(token))
12945     {
12946       result = get_hash_from_string(token);     // unsigned int => int
12947       result = ABS(result);                     // may be negative now
12948       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12949
12950       setHashEntry(anim_url_hash, int2str(result, 0), token);
12951     }
12952   }
12953
12954   if (result == -1)
12955     result = ANIM_EVENT_ACTION_NONE;
12956
12957   return result;
12958 }
12959
12960 int get_parameter_value(char *value_raw, char *suffix, int type)
12961 {
12962   char *value = getStringToLower(value_raw);
12963   int result = 0;       // probably a save default value
12964
12965   if (strEqual(suffix, ".direction"))
12966   {
12967     result = (strEqual(value, "left")  ? MV_LEFT :
12968               strEqual(value, "right") ? MV_RIGHT :
12969               strEqual(value, "up")    ? MV_UP :
12970               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12971   }
12972   else if (strEqual(suffix, ".position"))
12973   {
12974     result = (strEqual(value, "left")   ? POS_LEFT :
12975               strEqual(value, "right")  ? POS_RIGHT :
12976               strEqual(value, "top")    ? POS_TOP :
12977               strEqual(value, "upper")  ? POS_UPPER :
12978               strEqual(value, "middle") ? POS_MIDDLE :
12979               strEqual(value, "lower")  ? POS_LOWER :
12980               strEqual(value, "bottom") ? POS_BOTTOM :
12981               strEqual(value, "any")    ? POS_ANY :
12982               strEqual(value, "ce")     ? POS_CE :
12983               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12984               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12985   }
12986   else if (strEqual(suffix, ".align"))
12987   {
12988     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12989               strEqual(value, "right")  ? ALIGN_RIGHT :
12990               strEqual(value, "center") ? ALIGN_CENTER :
12991               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12992   }
12993   else if (strEqual(suffix, ".valign"))
12994   {
12995     result = (strEqual(value, "top")    ? VALIGN_TOP :
12996               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12997               strEqual(value, "middle") ? VALIGN_MIDDLE :
12998               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12999   }
13000   else if (strEqual(suffix, ".anim_mode"))
13001   {
13002     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
13003               string_has_parameter(value, "loop")       ? ANIM_LOOP :
13004               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
13005               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
13006               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
13007               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13008               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13009               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13010               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13011               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13012               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13013               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13014               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13015               string_has_parameter(value, "all")        ? ANIM_ALL :
13016               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13017               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13018               ANIM_DEFAULT);
13019
13020     if (string_has_parameter(value, "once"))
13021       result |= ANIM_ONCE;
13022
13023     if (string_has_parameter(value, "reverse"))
13024       result |= ANIM_REVERSE;
13025
13026     if (string_has_parameter(value, "opaque_player"))
13027       result |= ANIM_OPAQUE_PLAYER;
13028
13029     if (string_has_parameter(value, "static_panel"))
13030       result |= ANIM_STATIC_PANEL;
13031   }
13032   else if (strEqual(suffix, ".init_event") ||
13033            strEqual(suffix, ".anim_event"))
13034   {
13035     result = get_anim_parameter_values(value);
13036   }
13037   else if (strEqual(suffix, ".init_delay_action") ||
13038            strEqual(suffix, ".anim_delay_action") ||
13039            strEqual(suffix, ".post_delay_action") ||
13040            strEqual(suffix, ".init_event_action") ||
13041            strEqual(suffix, ".anim_event_action"))
13042   {
13043     result = get_anim_action_parameter_value(value_raw);
13044   }
13045   else if (strEqual(suffix, ".class"))
13046   {
13047     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13048               get_hash_from_string(value));
13049   }
13050   else if (strEqual(suffix, ".style"))
13051   {
13052     result = STYLE_DEFAULT;
13053
13054     if (string_has_parameter(value, "accurate_borders"))
13055       result |= STYLE_ACCURATE_BORDERS;
13056
13057     if (string_has_parameter(value, "inner_corners"))
13058       result |= STYLE_INNER_CORNERS;
13059
13060     if (string_has_parameter(value, "reverse"))
13061       result |= STYLE_REVERSE;
13062
13063     if (string_has_parameter(value, "leftmost_position"))
13064       result |= STYLE_LEFTMOST_POSITION;
13065
13066     if (string_has_parameter(value, "block_clicks"))
13067       result |= STYLE_BLOCK;
13068
13069     if (string_has_parameter(value, "passthrough_clicks"))
13070       result |= STYLE_PASSTHROUGH;
13071
13072     if (string_has_parameter(value, "multiple_actions"))
13073       result |= STYLE_MULTIPLE_ACTIONS;
13074
13075     if (string_has_parameter(value, "consume_ce_event"))
13076       result |= STYLE_CONSUME_CE_EVENT;
13077   }
13078   else if (strEqual(suffix, ".fade_mode"))
13079   {
13080     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13081               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13082               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13083               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13084               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13085               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13086               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13087               FADE_MODE_DEFAULT);
13088   }
13089   else if (strEqual(suffix, ".auto_delay_unit"))
13090   {
13091     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13092               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13093               AUTO_DELAY_UNIT_DEFAULT);
13094   }
13095   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13096   {
13097     result = gfx.get_font_from_token_function(value);
13098   }
13099   else          // generic parameter of type integer or boolean
13100   {
13101     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13102               type == TYPE_INTEGER ? get_integer_from_string(value) :
13103               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13104               ARG_UNDEFINED_VALUE);
13105   }
13106
13107   free(value);
13108
13109   return result;
13110 }
13111
13112 static int get_token_parameter_value(char *token, char *value_raw)
13113 {
13114   char *suffix;
13115
13116   if (token == NULL || value_raw == NULL)
13117     return ARG_UNDEFINED_VALUE;
13118
13119   suffix = strrchr(token, '.');
13120   if (suffix == NULL)
13121     suffix = token;
13122
13123   if (strEqual(suffix, ".element"))
13124     return getElementFromToken(value_raw);
13125
13126   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13127   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13128 }
13129
13130 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13131                                      boolean ignore_defaults)
13132 {
13133   int i;
13134
13135   for (i = 0; image_config_vars[i].token != NULL; i++)
13136   {
13137     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13138
13139     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13140     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13141       continue;
13142
13143     if (value != NULL)
13144       *image_config_vars[i].value =
13145         get_token_parameter_value(image_config_vars[i].token, value);
13146   }
13147 }
13148
13149 void InitMenuDesignSettings_Static(void)
13150 {
13151   // always start with reliable default values from static default config
13152   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13153 }
13154
13155 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13156 {
13157   int i;
13158
13159   // the following initializes hierarchical values from static configuration
13160
13161   // special case: initialize "ARG_DEFAULT" values in static default config
13162   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13163   titlescreen_initial_first_default.fade_mode  =
13164     title_initial_first_default.fade_mode;
13165   titlescreen_initial_first_default.fade_delay =
13166     title_initial_first_default.fade_delay;
13167   titlescreen_initial_first_default.post_delay =
13168     title_initial_first_default.post_delay;
13169   titlescreen_initial_first_default.auto_delay =
13170     title_initial_first_default.auto_delay;
13171   titlescreen_initial_first_default.auto_delay_unit =
13172     title_initial_first_default.auto_delay_unit;
13173   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13174   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13175   titlescreen_first_default.post_delay = title_first_default.post_delay;
13176   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13177   titlescreen_first_default.auto_delay_unit =
13178     title_first_default.auto_delay_unit;
13179   titlemessage_initial_first_default.fade_mode  =
13180     title_initial_first_default.fade_mode;
13181   titlemessage_initial_first_default.fade_delay =
13182     title_initial_first_default.fade_delay;
13183   titlemessage_initial_first_default.post_delay =
13184     title_initial_first_default.post_delay;
13185   titlemessage_initial_first_default.auto_delay =
13186     title_initial_first_default.auto_delay;
13187   titlemessage_initial_first_default.auto_delay_unit =
13188     title_initial_first_default.auto_delay_unit;
13189   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13190   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13191   titlemessage_first_default.post_delay = title_first_default.post_delay;
13192   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13193   titlemessage_first_default.auto_delay_unit =
13194     title_first_default.auto_delay_unit;
13195
13196   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13197   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13198   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13199   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13200   titlescreen_initial_default.auto_delay_unit =
13201     title_initial_default.auto_delay_unit;
13202   titlescreen_default.fade_mode  = title_default.fade_mode;
13203   titlescreen_default.fade_delay = title_default.fade_delay;
13204   titlescreen_default.post_delay = title_default.post_delay;
13205   titlescreen_default.auto_delay = title_default.auto_delay;
13206   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13207   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13208   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13209   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13210   titlemessage_initial_default.auto_delay_unit =
13211     title_initial_default.auto_delay_unit;
13212   titlemessage_default.fade_mode  = title_default.fade_mode;
13213   titlemessage_default.fade_delay = title_default.fade_delay;
13214   titlemessage_default.post_delay = title_default.post_delay;
13215   titlemessage_default.auto_delay = title_default.auto_delay;
13216   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13217
13218   // special case: initialize "ARG_DEFAULT" values in static default config
13219   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13220   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13221   {
13222     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13223     titlescreen_first[i] = titlescreen_first_default;
13224     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13225     titlemessage_first[i] = titlemessage_first_default;
13226
13227     titlescreen_initial[i] = titlescreen_initial_default;
13228     titlescreen[i] = titlescreen_default;
13229     titlemessage_initial[i] = titlemessage_initial_default;
13230     titlemessage[i] = titlemessage_default;
13231   }
13232
13233   // special case: initialize "ARG_DEFAULT" values in static default config
13234   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13235   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13236   {
13237     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13238       continue;
13239
13240     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13241     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13242     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13243   }
13244
13245   // special case: initialize "ARG_DEFAULT" values in static default config
13246   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13247   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13248   {
13249     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13250     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13251     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13252
13253     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13254       continue;
13255
13256     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13257   }
13258 }
13259
13260 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13261 {
13262   static struct
13263   {
13264     struct XY *dst, *src;
13265   }
13266   game_buttons_xy[] =
13267   {
13268     { &game.button.save,        &game.button.stop       },
13269     { &game.button.pause2,      &game.button.pause      },
13270     { &game.button.load,        &game.button.play       },
13271     { &game.button.undo,        &game.button.stop       },
13272     { &game.button.redo,        &game.button.play       },
13273
13274     { NULL,                     NULL                    }
13275   };
13276   int i, j;
13277
13278   // special case: initialize later added SETUP list size from LEVELS value
13279   if (menu.list_size[GAME_MODE_SETUP] == -1)
13280     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13281
13282   // set default position for snapshot buttons to stop/pause/play buttons
13283   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13284     if ((*game_buttons_xy[i].dst).x == -1 &&
13285         (*game_buttons_xy[i].dst).y == -1)
13286       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13287
13288   // --------------------------------------------------------------------------
13289   // dynamic viewports (including playfield margins, borders and alignments)
13290   // --------------------------------------------------------------------------
13291
13292   // dynamic viewports currently only supported for landscape mode
13293   int display_width  = MAX(video.display_width, video.display_height);
13294   int display_height = MIN(video.display_width, video.display_height);
13295
13296   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13297   {
13298     struct RectWithBorder *vp_window    = &viewport.window[i];
13299     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13300     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13301     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13302     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13303     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13304     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13305     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13306
13307     // adjust window size if min/max width/height is specified
13308
13309     if (vp_window->min_width != -1)
13310     {
13311       int window_width = display_width;
13312
13313       // when using static window height, use aspect ratio of display
13314       if (vp_window->min_height == -1)
13315         window_width = vp_window->height * display_width / display_height;
13316
13317       vp_window->width = MAX(vp_window->min_width, window_width);
13318     }
13319
13320     if (vp_window->min_height != -1)
13321     {
13322       int window_height = display_height;
13323
13324       // when using static window width, use aspect ratio of display
13325       if (vp_window->min_width == -1)
13326         window_height = vp_window->width * display_height / display_width;
13327
13328       vp_window->height = MAX(vp_window->min_height, window_height);
13329     }
13330
13331     if (vp_window->max_width != -1)
13332       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13333
13334     if (vp_window->max_height != -1)
13335       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13336
13337     int playfield_width  = vp_window->width;
13338     int playfield_height = vp_window->height;
13339
13340     // adjust playfield size and position according to specified margins
13341
13342     playfield_width  -= vp_playfield->margin_left;
13343     playfield_width  -= vp_playfield->margin_right;
13344
13345     playfield_height -= vp_playfield->margin_top;
13346     playfield_height -= vp_playfield->margin_bottom;
13347
13348     // adjust playfield size if min/max width/height is specified
13349
13350     if (vp_playfield->min_width != -1)
13351       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13352
13353     if (vp_playfield->min_height != -1)
13354       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13355
13356     if (vp_playfield->max_width != -1)
13357       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13358
13359     if (vp_playfield->max_height != -1)
13360       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13361
13362     // adjust playfield position according to specified alignment
13363
13364     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13365       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13366     else if (vp_playfield->align == ALIGN_CENTER)
13367       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13368     else if (vp_playfield->align == ALIGN_RIGHT)
13369       vp_playfield->x += playfield_width - vp_playfield->width;
13370
13371     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13372       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13373     else if (vp_playfield->valign == VALIGN_MIDDLE)
13374       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13375     else if (vp_playfield->valign == VALIGN_BOTTOM)
13376       vp_playfield->y += playfield_height - vp_playfield->height;
13377
13378     vp_playfield->x += vp_playfield->margin_left;
13379     vp_playfield->y += vp_playfield->margin_top;
13380
13381     // adjust individual playfield borders if only default border is specified
13382
13383     if (vp_playfield->border_left == -1)
13384       vp_playfield->border_left = vp_playfield->border_size;
13385     if (vp_playfield->border_right == -1)
13386       vp_playfield->border_right = vp_playfield->border_size;
13387     if (vp_playfield->border_top == -1)
13388       vp_playfield->border_top = vp_playfield->border_size;
13389     if (vp_playfield->border_bottom == -1)
13390       vp_playfield->border_bottom = vp_playfield->border_size;
13391
13392     // set dynamic playfield borders if borders are specified as undefined
13393     // (but only if window size was dynamic and playfield size was static)
13394
13395     if (dynamic_window_width && !dynamic_playfield_width)
13396     {
13397       if (vp_playfield->border_left == -1)
13398       {
13399         vp_playfield->border_left = (vp_playfield->x -
13400                                      vp_playfield->margin_left);
13401         vp_playfield->x     -= vp_playfield->border_left;
13402         vp_playfield->width += vp_playfield->border_left;
13403       }
13404
13405       if (vp_playfield->border_right == -1)
13406       {
13407         vp_playfield->border_right = (vp_window->width -
13408                                       vp_playfield->x -
13409                                       vp_playfield->width -
13410                                       vp_playfield->margin_right);
13411         vp_playfield->width += vp_playfield->border_right;
13412       }
13413     }
13414
13415     if (dynamic_window_height && !dynamic_playfield_height)
13416     {
13417       if (vp_playfield->border_top == -1)
13418       {
13419         vp_playfield->border_top = (vp_playfield->y -
13420                                     vp_playfield->margin_top);
13421         vp_playfield->y      -= vp_playfield->border_top;
13422         vp_playfield->height += vp_playfield->border_top;
13423       }
13424
13425       if (vp_playfield->border_bottom == -1)
13426       {
13427         vp_playfield->border_bottom = (vp_window->height -
13428                                        vp_playfield->y -
13429                                        vp_playfield->height -
13430                                        vp_playfield->margin_bottom);
13431         vp_playfield->height += vp_playfield->border_bottom;
13432       }
13433     }
13434
13435     // adjust playfield size to be a multiple of a defined alignment tile size
13436
13437     int align_size = vp_playfield->align_size;
13438     int playfield_xtiles = vp_playfield->width  / align_size;
13439     int playfield_ytiles = vp_playfield->height / align_size;
13440     int playfield_width_corrected  = playfield_xtiles * align_size;
13441     int playfield_height_corrected = playfield_ytiles * align_size;
13442     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13443                                  i == GFX_SPECIAL_ARG_EDITOR);
13444
13445     if (is_playfield_mode &&
13446         dynamic_playfield_width &&
13447         vp_playfield->width != playfield_width_corrected)
13448     {
13449       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13450
13451       vp_playfield->width = playfield_width_corrected;
13452
13453       if (vp_playfield->align == ALIGN_LEFT)
13454       {
13455         vp_playfield->border_left += playfield_xdiff;
13456       }
13457       else if (vp_playfield->align == ALIGN_RIGHT)
13458       {
13459         vp_playfield->border_right += playfield_xdiff;
13460       }
13461       else if (vp_playfield->align == ALIGN_CENTER)
13462       {
13463         int border_left_diff  = playfield_xdiff / 2;
13464         int border_right_diff = playfield_xdiff - border_left_diff;
13465
13466         vp_playfield->border_left  += border_left_diff;
13467         vp_playfield->border_right += border_right_diff;
13468       }
13469     }
13470
13471     if (is_playfield_mode &&
13472         dynamic_playfield_height &&
13473         vp_playfield->height != playfield_height_corrected)
13474     {
13475       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13476
13477       vp_playfield->height = playfield_height_corrected;
13478
13479       if (vp_playfield->valign == VALIGN_TOP)
13480       {
13481         vp_playfield->border_top += playfield_ydiff;
13482       }
13483       else if (vp_playfield->align == VALIGN_BOTTOM)
13484       {
13485         vp_playfield->border_right += playfield_ydiff;
13486       }
13487       else if (vp_playfield->align == VALIGN_MIDDLE)
13488       {
13489         int border_top_diff    = playfield_ydiff / 2;
13490         int border_bottom_diff = playfield_ydiff - border_top_diff;
13491
13492         vp_playfield->border_top    += border_top_diff;
13493         vp_playfield->border_bottom += border_bottom_diff;
13494       }
13495     }
13496
13497     // adjust door positions according to specified alignment
13498
13499     for (j = 0; j < 2; j++)
13500     {
13501       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13502
13503       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13504         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13505       else if (vp_door->align == ALIGN_CENTER)
13506         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13507       else if (vp_door->align == ALIGN_RIGHT)
13508         vp_door->x += vp_window->width - vp_door->width;
13509
13510       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13511         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13512       else if (vp_door->valign == VALIGN_MIDDLE)
13513         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13514       else if (vp_door->valign == VALIGN_BOTTOM)
13515         vp_door->y += vp_window->height - vp_door->height;
13516     }
13517   }
13518 }
13519
13520 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13521 {
13522   static struct
13523   {
13524     struct XYTileSize *dst, *src;
13525     int graphic;
13526   }
13527   editor_buttons_xy[] =
13528   {
13529     {
13530       &editor.button.element_left,      &editor.palette.element_left,
13531       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13532     },
13533     {
13534       &editor.button.element_middle,    &editor.palette.element_middle,
13535       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13536     },
13537     {
13538       &editor.button.element_right,     &editor.palette.element_right,
13539       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13540     },
13541
13542     { NULL,                     NULL                    }
13543   };
13544   int i;
13545
13546   // set default position for element buttons to element graphics
13547   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13548   {
13549     if ((*editor_buttons_xy[i].dst).x == -1 &&
13550         (*editor_buttons_xy[i].dst).y == -1)
13551     {
13552       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13553
13554       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13555
13556       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13557     }
13558   }
13559
13560   // adjust editor palette rows and columns if specified to be dynamic
13561
13562   if (editor.palette.cols == -1)
13563   {
13564     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13565     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13566     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13567
13568     editor.palette.cols = (vp_width - sc_width) / bt_width;
13569
13570     if (editor.palette.x == -1)
13571     {
13572       int palette_width = editor.palette.cols * bt_width + sc_width;
13573
13574       editor.palette.x = (vp_width - palette_width) / 2;
13575     }
13576   }
13577
13578   if (editor.palette.rows == -1)
13579   {
13580     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13581     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13582     int tx_height = getFontHeight(FONT_TEXT_2);
13583
13584     editor.palette.rows = (vp_height - tx_height) / bt_height;
13585
13586     if (editor.palette.y == -1)
13587     {
13588       int palette_height = editor.palette.rows * bt_height + tx_height;
13589
13590       editor.palette.y = (vp_height - palette_height) / 2;
13591     }
13592   }
13593 }
13594
13595 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13596                                                       boolean initialize)
13597 {
13598   // special case: check if network and preview player positions are redefined,
13599   // to compare this later against the main menu level preview being redefined
13600   struct TokenIntPtrInfo menu_config_players[] =
13601   {
13602     { "main.network_players.x", &menu.main.network_players.redefined    },
13603     { "main.network_players.y", &menu.main.network_players.redefined    },
13604     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13605     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13606     { "preview.x",              &preview.redefined                      },
13607     { "preview.y",              &preview.redefined                      }
13608   };
13609   int i;
13610
13611   if (initialize)
13612   {
13613     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13614       *menu_config_players[i].value = FALSE;
13615   }
13616   else
13617   {
13618     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13619       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13620         *menu_config_players[i].value = TRUE;
13621   }
13622 }
13623
13624 static void InitMenuDesignSettings_PreviewPlayers(void)
13625 {
13626   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13627 }
13628
13629 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13630 {
13631   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13632 }
13633
13634 static void LoadMenuDesignSettingsFromFilename(char *filename)
13635 {
13636   static struct TitleFadingInfo tfi;
13637   static struct TitleMessageInfo tmi;
13638   static struct TokenInfo title_tokens[] =
13639   {
13640     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13641     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13642     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13643     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13644     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13645
13646     { -1,               NULL,                   NULL                    }
13647   };
13648   static struct TokenInfo titlemessage_tokens[] =
13649   {
13650     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13651     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13652     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13653     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13654     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13655     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13656     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13657     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13658     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13659     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13660     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13661     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13662     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13663     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13664     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13665     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13666     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13667     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13668
13669     { -1,               NULL,                   NULL                    }
13670   };
13671   static struct
13672   {
13673     struct TitleFadingInfo *info;
13674     char *text;
13675   }
13676   title_info[] =
13677   {
13678     // initialize first titles from "enter screen" definitions, if defined
13679     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13680     { &title_first_default,             "menu.enter_screen.TITLE"       },
13681
13682     // initialize title screens from "next screen" definitions, if defined
13683     { &title_initial_default,           "menu.next_screen.TITLE"        },
13684     { &title_default,                   "menu.next_screen.TITLE"        },
13685
13686     { NULL,                             NULL                            }
13687   };
13688   static struct
13689   {
13690     struct TitleMessageInfo *array;
13691     char *text;
13692   }
13693   titlemessage_arrays[] =
13694   {
13695     // initialize first titles from "enter screen" definitions, if defined
13696     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13697     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13698     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13699     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13700
13701     // initialize titles from "next screen" definitions, if defined
13702     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13703     { titlescreen,                      "menu.next_screen.TITLE"        },
13704     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13705     { titlemessage,                     "menu.next_screen.TITLE"        },
13706
13707     // overwrite titles with title definitions, if defined
13708     { titlescreen_initial_first,        "[title_initial]"               },
13709     { titlescreen_first,                "[title]"                       },
13710     { titlemessage_initial_first,       "[title_initial]"               },
13711     { titlemessage_first,               "[title]"                       },
13712
13713     { titlescreen_initial,              "[title_initial]"               },
13714     { titlescreen,                      "[title]"                       },
13715     { titlemessage_initial,             "[title_initial]"               },
13716     { titlemessage,                     "[title]"                       },
13717
13718     // overwrite titles with title screen/message definitions, if defined
13719     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13720     { titlescreen_first,                "[titlescreen]"                 },
13721     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13722     { titlemessage_first,               "[titlemessage]"                },
13723
13724     { titlescreen_initial,              "[titlescreen_initial]"         },
13725     { titlescreen,                      "[titlescreen]"                 },
13726     { titlemessage_initial,             "[titlemessage_initial]"        },
13727     { titlemessage,                     "[titlemessage]"                },
13728
13729     { NULL,                             NULL                            }
13730   };
13731   SetupFileHash *setup_file_hash;
13732   int i, j, k;
13733
13734   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13735     return;
13736
13737   // the following initializes hierarchical values from dynamic configuration
13738
13739   // special case: initialize with default values that may be overwritten
13740   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13741   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13742   {
13743     struct TokenIntPtrInfo menu_config[] =
13744     {
13745       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13746       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13747       { "menu.list_size",       &menu.list_size[i]      }
13748     };
13749
13750     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13751     {
13752       char *token = menu_config[j].token;
13753       char *value = getHashEntry(setup_file_hash, token);
13754
13755       if (value != NULL)
13756         *menu_config[j].value = get_integer_from_string(value);
13757     }
13758   }
13759
13760   // special case: initialize with default values that may be overwritten
13761   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13762   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13763   {
13764     struct TokenIntPtrInfo menu_config[] =
13765     {
13766       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13767       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13768       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13769       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13770       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13771     };
13772
13773     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13774     {
13775       char *token = menu_config[j].token;
13776       char *value = getHashEntry(setup_file_hash, token);
13777
13778       if (value != NULL)
13779         *menu_config[j].value = get_integer_from_string(value);
13780     }
13781   }
13782
13783   // special case: initialize with default values that may be overwritten
13784   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13785   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13786   {
13787     struct TokenIntPtrInfo menu_config[] =
13788     {
13789       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13790       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13791     };
13792
13793     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13794     {
13795       char *token = menu_config[j].token;
13796       char *value = getHashEntry(setup_file_hash, token);
13797
13798       if (value != NULL)
13799         *menu_config[j].value = get_integer_from_string(value);
13800     }
13801   }
13802
13803   // special case: initialize with default values that may be overwritten
13804   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13805   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13806   {
13807     struct TokenIntPtrInfo menu_config[] =
13808     {
13809       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13810       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13811       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13812       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13813       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13814       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13815       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13816       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13817       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13818       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13819     };
13820
13821     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13822     {
13823       char *token = menu_config[j].token;
13824       char *value = getHashEntry(setup_file_hash, token);
13825
13826       if (value != NULL)
13827         *menu_config[j].value = get_integer_from_string(value);
13828     }
13829   }
13830
13831   // special case: initialize with default values that may be overwritten
13832   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13833   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13834   {
13835     struct TokenIntPtrInfo menu_config[] =
13836     {
13837       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13838       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13839       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13840       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13841       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13842       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13843       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13844       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13845       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13846     };
13847
13848     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13849     {
13850       char *token = menu_config[j].token;
13851       char *value = getHashEntry(setup_file_hash, token);
13852
13853       if (value != NULL)
13854         *menu_config[j].value = get_token_parameter_value(token, value);
13855     }
13856   }
13857
13858   // special case: initialize with default values that may be overwritten
13859   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13860   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13861   {
13862     struct
13863     {
13864       char *token_prefix;
13865       struct RectWithBorder *struct_ptr;
13866     }
13867     vp_struct[] =
13868     {
13869       { "viewport.window",      &viewport.window[i]     },
13870       { "viewport.playfield",   &viewport.playfield[i]  },
13871       { "viewport.door_1",      &viewport.door_1[i]     },
13872       { "viewport.door_2",      &viewport.door_2[i]     }
13873     };
13874
13875     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13876     {
13877       struct TokenIntPtrInfo vp_config[] =
13878       {
13879         { ".x",                 &vp_struct[j].struct_ptr->x             },
13880         { ".y",                 &vp_struct[j].struct_ptr->y             },
13881         { ".width",             &vp_struct[j].struct_ptr->width         },
13882         { ".height",            &vp_struct[j].struct_ptr->height        },
13883         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13884         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13885         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13886         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13887         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13888         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13889         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13890         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13891         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13892         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13893         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13894         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13895         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13896         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13897         { ".align",             &vp_struct[j].struct_ptr->align         },
13898         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13899       };
13900
13901       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13902       {
13903         char *token = getStringCat2(vp_struct[j].token_prefix,
13904                                     vp_config[k].token);
13905         char *value = getHashEntry(setup_file_hash, token);
13906
13907         if (value != NULL)
13908           *vp_config[k].value = get_token_parameter_value(token, value);
13909
13910         free(token);
13911       }
13912     }
13913   }
13914
13915   // special case: initialize with default values that may be overwritten
13916   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13917   for (i = 0; title_info[i].info != NULL; i++)
13918   {
13919     struct TitleFadingInfo *info = title_info[i].info;
13920     char *base_token = title_info[i].text;
13921
13922     for (j = 0; title_tokens[j].type != -1; j++)
13923     {
13924       char *token = getStringCat2(base_token, title_tokens[j].text);
13925       char *value = getHashEntry(setup_file_hash, token);
13926
13927       if (value != NULL)
13928       {
13929         int parameter_value = get_token_parameter_value(token, value);
13930
13931         tfi = *info;
13932
13933         *(int *)title_tokens[j].value = (int)parameter_value;
13934
13935         *info = tfi;
13936       }
13937
13938       free(token);
13939     }
13940   }
13941
13942   // special case: initialize with default values that may be overwritten
13943   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13944   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13945   {
13946     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13947     char *base_token = titlemessage_arrays[i].text;
13948
13949     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13950     {
13951       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13952       char *value = getHashEntry(setup_file_hash, token);
13953
13954       if (value != NULL)
13955       {
13956         int parameter_value = get_token_parameter_value(token, value);
13957
13958         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13959         {
13960           tmi = array[k];
13961
13962           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13963             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13964           else
13965             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13966
13967           array[k] = tmi;
13968         }
13969       }
13970
13971       free(token);
13972     }
13973   }
13974
13975   // read (and overwrite with) values that may be specified in config file
13976   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13977
13978   // special case: check if network and preview player positions are redefined
13979   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13980
13981   freeSetupFileHash(setup_file_hash);
13982 }
13983
13984 void LoadMenuDesignSettings(void)
13985 {
13986   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13987
13988   InitMenuDesignSettings_Static();
13989   InitMenuDesignSettings_SpecialPreProcessing();
13990   InitMenuDesignSettings_PreviewPlayers();
13991
13992   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13993   {
13994     // first look for special settings configured in level series config
13995     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13996
13997     if (fileExists(filename_base))
13998       LoadMenuDesignSettingsFromFilename(filename_base);
13999   }
14000
14001   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
14002
14003   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14004     LoadMenuDesignSettingsFromFilename(filename_local);
14005
14006   InitMenuDesignSettings_SpecialPostProcessing();
14007 }
14008
14009 void LoadMenuDesignSettings_AfterGraphics(void)
14010 {
14011   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14012 }
14013
14014 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14015                                 boolean ignore_defaults)
14016 {
14017   int i;
14018
14019   for (i = 0; sound_config_vars[i].token != NULL; i++)
14020   {
14021     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14022
14023     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14024     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14025       continue;
14026
14027     if (value != NULL)
14028       *sound_config_vars[i].value =
14029         get_token_parameter_value(sound_config_vars[i].token, value);
14030   }
14031 }
14032
14033 void InitSoundSettings_Static(void)
14034 {
14035   // always start with reliable default values from static default config
14036   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14037 }
14038
14039 static void LoadSoundSettingsFromFilename(char *filename)
14040 {
14041   SetupFileHash *setup_file_hash;
14042
14043   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14044     return;
14045
14046   // read (and overwrite with) values that may be specified in config file
14047   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14048
14049   freeSetupFileHash(setup_file_hash);
14050 }
14051
14052 void LoadSoundSettings(void)
14053 {
14054   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14055
14056   InitSoundSettings_Static();
14057
14058   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14059   {
14060     // first look for special settings configured in level series config
14061     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14062
14063     if (fileExists(filename_base))
14064       LoadSoundSettingsFromFilename(filename_base);
14065   }
14066
14067   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14068
14069   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14070     LoadSoundSettingsFromFilename(filename_local);
14071 }
14072
14073 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14074 {
14075   char *filename = getEditorSetupFilename();
14076   SetupFileList *setup_file_list, *list;
14077   SetupFileHash *element_hash;
14078   int num_unknown_tokens = 0;
14079   int i;
14080
14081   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14082     return;
14083
14084   element_hash = newSetupFileHash();
14085
14086   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14087     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14088
14089   // determined size may be larger than needed (due to unknown elements)
14090   *num_elements = 0;
14091   for (list = setup_file_list; list != NULL; list = list->next)
14092     (*num_elements)++;
14093
14094   // add space for up to 3 more elements for padding that may be needed
14095   *num_elements += 3;
14096
14097   // free memory for old list of elements, if needed
14098   checked_free(*elements);
14099
14100   // allocate memory for new list of elements
14101   *elements = checked_malloc(*num_elements * sizeof(int));
14102
14103   *num_elements = 0;
14104   for (list = setup_file_list; list != NULL; list = list->next)
14105   {
14106     char *value = getHashEntry(element_hash, list->token);
14107
14108     if (value == NULL)          // try to find obsolete token mapping
14109     {
14110       char *mapped_token = get_mapped_token(list->token);
14111
14112       if (mapped_token != NULL)
14113       {
14114         value = getHashEntry(element_hash, mapped_token);
14115
14116         free(mapped_token);
14117       }
14118     }
14119
14120     if (value != NULL)
14121     {
14122       (*elements)[(*num_elements)++] = atoi(value);
14123     }
14124     else
14125     {
14126       if (num_unknown_tokens == 0)
14127       {
14128         Warn("---");
14129         Warn("unknown token(s) found in config file:");
14130         Warn("- config file: '%s'", filename);
14131
14132         num_unknown_tokens++;
14133       }
14134
14135       Warn("- token: '%s'", list->token);
14136     }
14137   }
14138
14139   if (num_unknown_tokens > 0)
14140     Warn("---");
14141
14142   while (*num_elements % 4)     // pad with empty elements, if needed
14143     (*elements)[(*num_elements)++] = EL_EMPTY;
14144
14145   freeSetupFileList(setup_file_list);
14146   freeSetupFileHash(element_hash);
14147
14148 #if 0
14149   for (i = 0; i < *num_elements; i++)
14150     Debug("editor", "element '%s' [%d]\n",
14151           element_info[(*elements)[i]].token_name, (*elements)[i]);
14152 #endif
14153 }
14154
14155 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14156                                                      boolean is_sound)
14157 {
14158   SetupFileHash *setup_file_hash = NULL;
14159   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14160   char *filename_music, *filename_prefix, *filename_info;
14161   struct
14162   {
14163     char *token;
14164     char **value_ptr;
14165   }
14166   token_to_value_ptr[] =
14167   {
14168     { "title_header",   &tmp_music_file_info.title_header       },
14169     { "artist_header",  &tmp_music_file_info.artist_header      },
14170     { "album_header",   &tmp_music_file_info.album_header       },
14171     { "year_header",    &tmp_music_file_info.year_header        },
14172     { "played_header",  &tmp_music_file_info.played_header      },
14173
14174     { "title",          &tmp_music_file_info.title              },
14175     { "artist",         &tmp_music_file_info.artist             },
14176     { "album",          &tmp_music_file_info.album              },
14177     { "year",           &tmp_music_file_info.year               },
14178     { "played",         &tmp_music_file_info.played             },
14179
14180     { NULL,             NULL                                    },
14181   };
14182   int i;
14183
14184   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14185                     getCustomMusicFilename(basename));
14186
14187   if (filename_music == NULL)
14188     return NULL;
14189
14190   // ---------- try to replace file extension ----------
14191
14192   filename_prefix = getStringCopy(filename_music);
14193   if (strrchr(filename_prefix, '.') != NULL)
14194     *strrchr(filename_prefix, '.') = '\0';
14195   filename_info = getStringCat2(filename_prefix, ".txt");
14196
14197   if (fileExists(filename_info))
14198     setup_file_hash = loadSetupFileHash(filename_info);
14199
14200   free(filename_prefix);
14201   free(filename_info);
14202
14203   if (setup_file_hash == NULL)
14204   {
14205     // ---------- try to add file extension ----------
14206
14207     filename_prefix = getStringCopy(filename_music);
14208     filename_info = getStringCat2(filename_prefix, ".txt");
14209
14210     if (fileExists(filename_info))
14211       setup_file_hash = loadSetupFileHash(filename_info);
14212
14213     free(filename_prefix);
14214     free(filename_info);
14215   }
14216
14217   if (setup_file_hash == NULL)
14218     return NULL;
14219
14220   // ---------- music file info found ----------
14221
14222   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14223
14224   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14225   {
14226     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14227
14228     *token_to_value_ptr[i].value_ptr =
14229       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14230   }
14231
14232   tmp_music_file_info.basename = getStringCopy(basename);
14233   tmp_music_file_info.music = music;
14234   tmp_music_file_info.is_sound = is_sound;
14235
14236   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14237   *new_music_file_info = tmp_music_file_info;
14238
14239   return new_music_file_info;
14240 }
14241
14242 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14243 {
14244   return get_music_file_info_ext(basename, music, FALSE);
14245 }
14246
14247 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14248 {
14249   return get_music_file_info_ext(basename, sound, TRUE);
14250 }
14251
14252 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14253                                      char *basename, boolean is_sound)
14254 {
14255   for (; list != NULL; list = list->next)
14256     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14257       return TRUE;
14258
14259   return FALSE;
14260 }
14261
14262 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14263 {
14264   return music_info_listed_ext(list, basename, FALSE);
14265 }
14266
14267 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14268 {
14269   return music_info_listed_ext(list, basename, TRUE);
14270 }
14271
14272 void LoadMusicInfo(void)
14273 {
14274   int num_music_noconf = getMusicListSize_NoConf();
14275   int num_music = getMusicListSize();
14276   int num_sounds = getSoundListSize();
14277   struct FileInfo *music, *sound;
14278   struct MusicFileInfo *next, **new;
14279
14280   int i;
14281
14282   while (music_file_info != NULL)
14283   {
14284     next = music_file_info->next;
14285
14286     checked_free(music_file_info->basename);
14287
14288     checked_free(music_file_info->title_header);
14289     checked_free(music_file_info->artist_header);
14290     checked_free(music_file_info->album_header);
14291     checked_free(music_file_info->year_header);
14292     checked_free(music_file_info->played_header);
14293
14294     checked_free(music_file_info->title);
14295     checked_free(music_file_info->artist);
14296     checked_free(music_file_info->album);
14297     checked_free(music_file_info->year);
14298     checked_free(music_file_info->played);
14299
14300     free(music_file_info);
14301
14302     music_file_info = next;
14303   }
14304
14305   new = &music_file_info;
14306
14307   // get (configured or unconfigured) music file info for all levels
14308   for (i = leveldir_current->first_level;
14309        i <= leveldir_current->last_level; i++)
14310   {
14311     int music_nr;
14312
14313     if (levelset.music[i] != MUS_UNDEFINED)
14314     {
14315       // get music file info for configured level music
14316       music_nr = levelset.music[i];
14317     }
14318     else if (num_music_noconf > 0)
14319     {
14320       // get music file info for unconfigured level music
14321       int level_pos = i - leveldir_current->first_level;
14322
14323       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14324     }
14325     else
14326     {
14327       continue;
14328     }
14329
14330     char *basename = getMusicInfoEntryFilename(music_nr);
14331
14332     if (basename == NULL)
14333       continue;
14334
14335     if (!music_info_listed(music_file_info, basename))
14336     {
14337       *new = get_music_file_info(basename, music_nr);
14338
14339       if (*new != NULL)
14340         new = &(*new)->next;
14341     }
14342   }
14343
14344   // get music file info for all remaining configured music files
14345   for (i = 0; i < num_music; i++)
14346   {
14347     music = getMusicListEntry(i);
14348
14349     if (music->filename == NULL)
14350       continue;
14351
14352     if (strEqual(music->filename, UNDEFINED_FILENAME))
14353       continue;
14354
14355     // a configured file may be not recognized as music
14356     if (!FileIsMusic(music->filename))
14357       continue;
14358
14359     if (!music_info_listed(music_file_info, music->filename))
14360     {
14361       *new = get_music_file_info(music->filename, i);
14362
14363       if (*new != NULL)
14364         new = &(*new)->next;
14365     }
14366   }
14367
14368   // get sound file info for all configured sound files
14369   for (i = 0; i < num_sounds; i++)
14370   {
14371     sound = getSoundListEntry(i);
14372
14373     if (sound->filename == NULL)
14374       continue;
14375
14376     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14377       continue;
14378
14379     // a configured file may be not recognized as sound
14380     if (!FileIsSound(sound->filename))
14381       continue;
14382
14383     if (!sound_info_listed(music_file_info, sound->filename))
14384     {
14385       *new = get_sound_file_info(sound->filename, i);
14386       if (*new != NULL)
14387         new = &(*new)->next;
14388     }
14389   }
14390
14391   // add pointers to previous list nodes
14392
14393   struct MusicFileInfo *node = music_file_info;
14394
14395   while (node != NULL)
14396   {
14397     if (node->next)
14398       node->next->prev = node;
14399
14400     node = node->next;
14401   }
14402 }
14403
14404 static void add_helpanim_entry(int element, int action, int direction,
14405                                int delay, int *num_list_entries)
14406 {
14407   struct HelpAnimInfo *new_list_entry;
14408   (*num_list_entries)++;
14409
14410   helpanim_info =
14411     checked_realloc(helpanim_info,
14412                     *num_list_entries * sizeof(struct HelpAnimInfo));
14413   new_list_entry = &helpanim_info[*num_list_entries - 1];
14414
14415   new_list_entry->element = element;
14416   new_list_entry->action = action;
14417   new_list_entry->direction = direction;
14418   new_list_entry->delay = delay;
14419 }
14420
14421 static void print_unknown_token(char *filename, char *token, int token_nr)
14422 {
14423   if (token_nr == 0)
14424   {
14425     Warn("---");
14426     Warn("unknown token(s) found in config file:");
14427     Warn("- config file: '%s'", filename);
14428   }
14429
14430   Warn("- token: '%s'", token);
14431 }
14432
14433 static void print_unknown_token_end(int token_nr)
14434 {
14435   if (token_nr > 0)
14436     Warn("---");
14437 }
14438
14439 void LoadHelpAnimInfo(void)
14440 {
14441   char *filename = getHelpAnimFilename();
14442   SetupFileList *setup_file_list = NULL, *list;
14443   SetupFileHash *element_hash, *action_hash, *direction_hash;
14444   int num_list_entries = 0;
14445   int num_unknown_tokens = 0;
14446   int i;
14447
14448   if (fileExists(filename))
14449     setup_file_list = loadSetupFileList(filename);
14450
14451   if (setup_file_list == NULL)
14452   {
14453     // use reliable default values from static configuration
14454     SetupFileList *insert_ptr;
14455
14456     insert_ptr = setup_file_list =
14457       newSetupFileList(helpanim_config[0].token,
14458                        helpanim_config[0].value);
14459
14460     for (i = 1; helpanim_config[i].token; i++)
14461       insert_ptr = addListEntry(insert_ptr,
14462                                 helpanim_config[i].token,
14463                                 helpanim_config[i].value);
14464   }
14465
14466   element_hash   = newSetupFileHash();
14467   action_hash    = newSetupFileHash();
14468   direction_hash = newSetupFileHash();
14469
14470   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14471     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14472
14473   for (i = 0; i < NUM_ACTIONS; i++)
14474     setHashEntry(action_hash, element_action_info[i].suffix,
14475                  i_to_a(element_action_info[i].value));
14476
14477   // do not store direction index (bit) here, but direction value!
14478   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14479     setHashEntry(direction_hash, element_direction_info[i].suffix,
14480                  i_to_a(1 << element_direction_info[i].value));
14481
14482   for (list = setup_file_list; list != NULL; list = list->next)
14483   {
14484     char *element_token, *action_token, *direction_token;
14485     char *element_value, *action_value, *direction_value;
14486     int delay = atoi(list->value);
14487
14488     if (strEqual(list->token, "end"))
14489     {
14490       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14491
14492       continue;
14493     }
14494
14495     /* first try to break element into element/action/direction parts;
14496        if this does not work, also accept combined "element[.act][.dir]"
14497        elements (like "dynamite.active"), which are unique elements */
14498
14499     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14500     {
14501       element_value = getHashEntry(element_hash, list->token);
14502       if (element_value != NULL)        // element found
14503         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14504                            &num_list_entries);
14505       else
14506       {
14507         // no further suffixes found -- this is not an element
14508         print_unknown_token(filename, list->token, num_unknown_tokens++);
14509       }
14510
14511       continue;
14512     }
14513
14514     // token has format "<prefix>.<something>"
14515
14516     action_token = strchr(list->token, '.');    // suffix may be action ...
14517     direction_token = action_token;             // ... or direction
14518
14519     element_token = getStringCopy(list->token);
14520     *strchr(element_token, '.') = '\0';
14521
14522     element_value = getHashEntry(element_hash, element_token);
14523
14524     if (element_value == NULL)          // this is no element
14525     {
14526       element_value = getHashEntry(element_hash, list->token);
14527       if (element_value != NULL)        // combined element found
14528         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14529                            &num_list_entries);
14530       else
14531         print_unknown_token(filename, list->token, num_unknown_tokens++);
14532
14533       free(element_token);
14534
14535       continue;
14536     }
14537
14538     action_value = getHashEntry(action_hash, action_token);
14539
14540     if (action_value != NULL)           // action found
14541     {
14542       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14543                     &num_list_entries);
14544
14545       free(element_token);
14546
14547       continue;
14548     }
14549
14550     direction_value = getHashEntry(direction_hash, direction_token);
14551
14552     if (direction_value != NULL)        // direction found
14553     {
14554       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14555                          &num_list_entries);
14556
14557       free(element_token);
14558
14559       continue;
14560     }
14561
14562     if (strchr(action_token + 1, '.') == NULL)
14563     {
14564       // no further suffixes found -- this is not an action nor direction
14565
14566       element_value = getHashEntry(element_hash, list->token);
14567       if (element_value != NULL)        // combined element found
14568         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14569                            &num_list_entries);
14570       else
14571         print_unknown_token(filename, list->token, num_unknown_tokens++);
14572
14573       free(element_token);
14574
14575       continue;
14576     }
14577
14578     // token has format "<prefix>.<suffix>.<something>"
14579
14580     direction_token = strchr(action_token + 1, '.');
14581
14582     action_token = getStringCopy(action_token);
14583     *strchr(action_token + 1, '.') = '\0';
14584
14585     action_value = getHashEntry(action_hash, action_token);
14586
14587     if (action_value == NULL)           // this is no action
14588     {
14589       element_value = getHashEntry(element_hash, list->token);
14590       if (element_value != NULL)        // combined element found
14591         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14592                            &num_list_entries);
14593       else
14594         print_unknown_token(filename, list->token, num_unknown_tokens++);
14595
14596       free(element_token);
14597       free(action_token);
14598
14599       continue;
14600     }
14601
14602     direction_value = getHashEntry(direction_hash, direction_token);
14603
14604     if (direction_value != NULL)        // direction found
14605     {
14606       add_helpanim_entry(atoi(element_value), atoi(action_value),
14607                          atoi(direction_value), delay, &num_list_entries);
14608
14609       free(element_token);
14610       free(action_token);
14611
14612       continue;
14613     }
14614
14615     // this is no direction
14616
14617     element_value = getHashEntry(element_hash, list->token);
14618     if (element_value != NULL)          // combined element found
14619       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14620                          &num_list_entries);
14621     else
14622       print_unknown_token(filename, list->token, num_unknown_tokens++);
14623
14624     free(element_token);
14625     free(action_token);
14626   }
14627
14628   print_unknown_token_end(num_unknown_tokens);
14629
14630   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14631   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14632
14633   freeSetupFileList(setup_file_list);
14634   freeSetupFileHash(element_hash);
14635   freeSetupFileHash(action_hash);
14636   freeSetupFileHash(direction_hash);
14637
14638 #if 0
14639   for (i = 0; i < num_list_entries; i++)
14640     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14641           EL_NAME(helpanim_info[i].element),
14642           helpanim_info[i].element,
14643           helpanim_info[i].action,
14644           helpanim_info[i].direction,
14645           helpanim_info[i].delay);
14646 #endif
14647 }
14648
14649 void LoadHelpTextInfo(void)
14650 {
14651   char *filename = getHelpTextFilename();
14652   int i;
14653
14654   if (helptext_info != NULL)
14655   {
14656     freeSetupFileHash(helptext_info);
14657     helptext_info = NULL;
14658   }
14659
14660   if (fileExists(filename))
14661     helptext_info = loadSetupFileHash(filename);
14662
14663   if (helptext_info == NULL)
14664   {
14665     // use reliable default values from static configuration
14666     helptext_info = newSetupFileHash();
14667
14668     for (i = 0; helptext_config[i].token; i++)
14669       setHashEntry(helptext_info,
14670                    helptext_config[i].token,
14671                    helptext_config[i].value);
14672   }
14673
14674 #if 0
14675   BEGIN_HASH_ITERATION(helptext_info, itr)
14676   {
14677     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14678           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14679   }
14680   END_HASH_ITERATION(hash, itr)
14681 #endif
14682 }
14683
14684
14685 // ----------------------------------------------------------------------------
14686 // convert levels
14687 // ----------------------------------------------------------------------------
14688
14689 #define MAX_NUM_CONVERT_LEVELS          1000
14690
14691 void ConvertLevels(void)
14692 {
14693   static LevelDirTree *convert_leveldir = NULL;
14694   static int convert_level_nr = -1;
14695   static int num_levels_handled = 0;
14696   static int num_levels_converted = 0;
14697   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14698   int i;
14699
14700   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14701                                                global.convert_leveldir);
14702
14703   if (convert_leveldir == NULL)
14704     Fail("no such level identifier: '%s'", global.convert_leveldir);
14705
14706   leveldir_current = convert_leveldir;
14707
14708   if (global.convert_level_nr != -1)
14709   {
14710     convert_leveldir->first_level = global.convert_level_nr;
14711     convert_leveldir->last_level  = global.convert_level_nr;
14712   }
14713
14714   convert_level_nr = convert_leveldir->first_level;
14715
14716   PrintLine("=", 79);
14717   Print("Converting levels\n");
14718   PrintLine("-", 79);
14719   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14720   Print("Level series name:       '%s'\n", convert_leveldir->name);
14721   Print("Level series author:     '%s'\n", convert_leveldir->author);
14722   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14723   PrintLine("=", 79);
14724   Print("\n");
14725
14726   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14727     levels_failed[i] = FALSE;
14728
14729   while (convert_level_nr <= convert_leveldir->last_level)
14730   {
14731     char *level_filename;
14732     boolean new_level;
14733
14734     level_nr = convert_level_nr++;
14735
14736     Print("Level %03d: ", level_nr);
14737
14738     LoadLevel(level_nr);
14739     if (level.no_level_file || level.no_valid_file)
14740     {
14741       Print("(no level)\n");
14742       continue;
14743     }
14744
14745     Print("converting level ... ");
14746
14747 #if 0
14748     // special case: conversion of some EMC levels as requested by ACME
14749     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14750 #endif
14751
14752     level_filename = getDefaultLevelFilename(level_nr);
14753     new_level = !fileExists(level_filename);
14754
14755     if (new_level)
14756     {
14757       SaveLevel(level_nr);
14758
14759       num_levels_converted++;
14760
14761       Print("converted.\n");
14762     }
14763     else
14764     {
14765       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14766         levels_failed[level_nr] = TRUE;
14767
14768       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14769     }
14770
14771     num_levels_handled++;
14772   }
14773
14774   Print("\n");
14775   PrintLine("=", 79);
14776   Print("Number of levels handled: %d\n", num_levels_handled);
14777   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14778          (num_levels_handled ?
14779           num_levels_converted * 100 / num_levels_handled : 0));
14780   PrintLine("-", 79);
14781   Print("Summary (for automatic parsing by scripts):\n");
14782   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14783          convert_leveldir->identifier, num_levels_converted,
14784          num_levels_handled,
14785          (num_levels_handled ?
14786           num_levels_converted * 100 / num_levels_handled : 0));
14787
14788   if (num_levels_handled != num_levels_converted)
14789   {
14790     Print(", FAILED:");
14791     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14792       if (levels_failed[i])
14793         Print(" %03d", i);
14794   }
14795
14796   Print("\n");
14797   PrintLine("=", 79);
14798
14799   CloseAllAndExit(0);
14800 }
14801
14802
14803 // ----------------------------------------------------------------------------
14804 // create and save images for use in level sketches (raw BMP format)
14805 // ----------------------------------------------------------------------------
14806
14807 void CreateLevelSketchImages(void)
14808 {
14809   Bitmap *bitmap1;
14810   Bitmap *bitmap2;
14811   int i;
14812
14813   InitElementPropertiesGfxElement();
14814
14815   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14816   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14817
14818   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14819   {
14820     int element = getMappedElement(i);
14821     char basename1[16];
14822     char basename2[16];
14823     char *filename1;
14824     char *filename2;
14825
14826     sprintf(basename1, "%04d.bmp", i);
14827     sprintf(basename2, "%04ds.bmp", i);
14828
14829     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14830     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14831
14832     DrawSizedElement(0, 0, element, TILESIZE);
14833     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14834
14835     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14836       Fail("cannot save level sketch image file '%s'", filename1);
14837
14838     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14839     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14840
14841     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14842       Fail("cannot save level sketch image file '%s'", filename2);
14843
14844     free(filename1);
14845     free(filename2);
14846
14847     // create corresponding SQL statements (for normal and small images)
14848     if (i < 1000)
14849     {
14850       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14851       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14852     }
14853
14854     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14855     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14856
14857     // optional: create content for forum level sketch demonstration post
14858     if (options.debug)
14859       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14860   }
14861
14862   FreeBitmap(bitmap1);
14863   FreeBitmap(bitmap2);
14864
14865   if (options.debug)
14866     fprintf(stderr, "\n");
14867
14868   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14869
14870   CloseAllAndExit(0);
14871 }
14872
14873
14874 // ----------------------------------------------------------------------------
14875 // create and save images for element collecting animations (raw BMP format)
14876 // ----------------------------------------------------------------------------
14877
14878 static boolean createCollectImage(int element)
14879 {
14880   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14881 }
14882
14883 void CreateCollectElementImages(void)
14884 {
14885   int i, j;
14886   int num_steps = 8;
14887   int anim_frames = num_steps - 1;
14888   int tile_size = TILESIZE;
14889   int anim_width  = tile_size * anim_frames;
14890   int anim_height = tile_size;
14891   int num_collect_images = 0;
14892   int pos_collect_images = 0;
14893
14894   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14895     if (createCollectImage(i))
14896       num_collect_images++;
14897
14898   Info("Creating %d element collecting animation images ...",
14899        num_collect_images);
14900
14901   int dst_width  = anim_width * 2;
14902   int dst_height = anim_height * num_collect_images / 2;
14903   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14904   char *basename_bmp = "RocksCollect.bmp";
14905   char *basename_png = "RocksCollect.png";
14906   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14907   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14908   int len_filename_bmp = strlen(filename_bmp);
14909   int len_filename_png = strlen(filename_png);
14910   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14911   char cmd_convert[max_command_len];
14912
14913   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14914            filename_bmp,
14915            filename_png);
14916
14917   // force using RGBA surface for destination bitmap
14918   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14919                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14920
14921   dst_bitmap->surface =
14922     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14923
14924   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14925   {
14926     if (!createCollectImage(i))
14927       continue;
14928
14929     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14930     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14931     int graphic = el2img(i);
14932     char *token_name = element_info[i].token_name;
14933     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14934     Bitmap *src_bitmap;
14935     int src_x, src_y;
14936
14937     Info("- creating collecting image for '%s' ...", token_name);
14938
14939     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14940
14941     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14942                tile_size, tile_size, 0, 0);
14943
14944     // force using RGBA surface for temporary bitmap (using transparent black)
14945     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14946                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14947
14948     tmp_bitmap->surface =
14949       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14950
14951     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14952
14953     for (j = 0; j < anim_frames; j++)
14954     {
14955       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14956       int frame_size = frame_size_final * num_steps;
14957       int offset = (tile_size - frame_size_final) / 2;
14958       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14959
14960       while (frame_size > frame_size_final)
14961       {
14962         frame_size /= 2;
14963
14964         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14965
14966         FreeBitmap(frame_bitmap);
14967
14968         frame_bitmap = half_bitmap;
14969       }
14970
14971       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14972                        frame_size_final, frame_size_final,
14973                        dst_x + j * tile_size + offset, dst_y + offset);
14974
14975       FreeBitmap(frame_bitmap);
14976     }
14977
14978     tmp_bitmap->surface_masked = NULL;
14979
14980     FreeBitmap(tmp_bitmap);
14981
14982     pos_collect_images++;
14983   }
14984
14985   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14986     Fail("cannot save element collecting image file '%s'", filename_bmp);
14987
14988   FreeBitmap(dst_bitmap);
14989
14990   Info("Converting image file from BMP to PNG ...");
14991
14992   if (system(cmd_convert) != 0)
14993     Fail("converting image file failed");
14994
14995   unlink(filename_bmp);
14996
14997   Info("Done.");
14998
14999   CloseAllAndExit(0);
15000 }
15001
15002
15003 // ----------------------------------------------------------------------------
15004 // create and save images for custom and group elements (raw BMP format)
15005 // ----------------------------------------------------------------------------
15006
15007 void CreateCustomElementImages(char *directory)
15008 {
15009   char *src_basename = "RocksCE-template.ilbm";
15010   char *dst_basename = "RocksCE.bmp";
15011   char *src_filename = getPath2(directory, src_basename);
15012   char *dst_filename = getPath2(directory, dst_basename);
15013   Bitmap *src_bitmap;
15014   Bitmap *bitmap;
15015   int yoffset_ce = 0;
15016   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15017   int i;
15018
15019   InitVideoDefaults();
15020
15021   ReCreateBitmap(&backbuffer, video.width, video.height);
15022
15023   src_bitmap = LoadImage(src_filename);
15024
15025   bitmap = CreateBitmap(TILEX * 16 * 2,
15026                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15027                         DEFAULT_DEPTH);
15028
15029   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15030   {
15031     int x = i % 16;
15032     int y = i / 16;
15033     int ii = i + 1;
15034     int j;
15035
15036     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15037                TILEX * x, TILEY * y + yoffset_ce);
15038
15039     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15040                TILEX, TILEY,
15041                TILEX * x + TILEX * 16,
15042                TILEY * y + yoffset_ce);
15043
15044     for (j = 2; j >= 0; j--)
15045     {
15046       int c = ii % 10;
15047
15048       BlitBitmap(src_bitmap, bitmap,
15049                  TILEX + c * 7, 0, 6, 10,
15050                  TILEX * x + 6 + j * 7,
15051                  TILEY * y + 11 + yoffset_ce);
15052
15053       BlitBitmap(src_bitmap, bitmap,
15054                  TILEX + c * 8, TILEY, 6, 10,
15055                  TILEX * 16 + TILEX * x + 6 + j * 8,
15056                  TILEY * y + 10 + yoffset_ce);
15057
15058       ii /= 10;
15059     }
15060   }
15061
15062   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15063   {
15064     int x = i % 16;
15065     int y = i / 16;
15066     int ii = i + 1;
15067     int j;
15068
15069     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15070                TILEX * x, TILEY * y + yoffset_ge);
15071
15072     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15073                TILEX, TILEY,
15074                TILEX * x + TILEX * 16,
15075                TILEY * y + yoffset_ge);
15076
15077     for (j = 1; j >= 0; j--)
15078     {
15079       int c = ii % 10;
15080
15081       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15082                  TILEX * x + 6 + j * 10,
15083                  TILEY * y + 11 + yoffset_ge);
15084
15085       BlitBitmap(src_bitmap, bitmap,
15086                  TILEX + c * 8, TILEY + 12, 6, 10,
15087                  TILEX * 16 + TILEX * x + 10 + j * 8,
15088                  TILEY * y + 10 + yoffset_ge);
15089
15090       ii /= 10;
15091     }
15092   }
15093
15094   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15095     Fail("cannot save CE graphics file '%s'", dst_filename);
15096
15097   FreeBitmap(bitmap);
15098
15099   CloseAllAndExit(0);
15100 }