renamed setup option token
[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 level playfield to playable default level with player and exit
2373   for (x = 0; x < MAX_LEV_FIELDX; x++)
2374     for (y = 0; y < MAX_LEV_FIELDY; y++)
2375       level->field[x][y] = EL_SAND;
2376
2377   level->field[0][0] = EL_PLAYER_1;
2378   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2379
2380   BorderElement = EL_STEELWALL;
2381
2382   // detect custom elements when loading them
2383   level->file_has_custom_elements = FALSE;
2384
2385   // set random colors for BD style levels according to preferred color type
2386   SetRandomLevelColors_BD(setup.bd_default_color_type);
2387
2388   // set default color type and colors for BD style level colors
2389   SetDefaultLevelColorType_BD();
2390   SetDefaultLevelColors_BD();
2391
2392   // set all bug compatibility flags to "false" => do not emulate this bug
2393   level->use_action_after_change_bug = FALSE;
2394
2395   if (leveldir_current)
2396   {
2397     // try to determine better author name than 'anonymous'
2398     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2399     {
2400       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2401       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2402     }
2403     else
2404     {
2405       switch (LEVELCLASS(leveldir_current))
2406       {
2407         case LEVELCLASS_TUTORIAL:
2408           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2409           break;
2410
2411         case LEVELCLASS_CONTRIB:
2412           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2413           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2414           break;
2415
2416         case LEVELCLASS_PRIVATE:
2417           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2418           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2419           break;
2420
2421         default:
2422           // keep default value
2423           break;
2424       }
2425     }
2426   }
2427 }
2428
2429 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2430 {
2431   static boolean clipboard_elements_initialized = FALSE;
2432   int i;
2433
2434   InitElementPropertiesStatic();
2435
2436   li = *level;          // copy level data into temporary buffer
2437   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2438   *level = li;          // copy temporary buffer back to level data
2439
2440   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2441   {
2442     int element = i;
2443     struct ElementInfo *ei = &element_info[element];
2444
2445     if (element == EL_MM_GRAY_BALL)
2446     {
2447       struct LevelInfo_MM *level_mm = level->native_mm_level;
2448       int j;
2449
2450       for (j = 0; j < level->num_mm_ball_contents; j++)
2451         level->mm_ball_content[j] =
2452           map_element_MM_to_RND(level_mm->ball_content[j]);
2453     }
2454
2455     // never initialize clipboard elements after the very first time
2456     // (to be able to use clipboard elements between several levels)
2457     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2458       continue;
2459
2460     if (IS_ENVELOPE(element))
2461     {
2462       int envelope_nr = element - EL_ENVELOPE_1;
2463
2464       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2465
2466       level->envelope[envelope_nr] = xx_envelope;
2467     }
2468
2469     if (IS_CUSTOM_ELEMENT(element) ||
2470         IS_GROUP_ELEMENT(element) ||
2471         IS_INTERNAL_ELEMENT(element))
2472     {
2473       xx_ei = *ei;      // copy element data into temporary buffer
2474
2475       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2476
2477       *ei = xx_ei;
2478     }
2479
2480     setElementChangePages(ei, 1);
2481     setElementChangeInfoToDefaults(ei->change);
2482
2483     if (IS_CUSTOM_ELEMENT(element) ||
2484         IS_GROUP_ELEMENT(element))
2485     {
2486       setElementDescriptionToDefault(ei);
2487
2488       ei->modified_settings = FALSE;
2489     }
2490
2491     if (IS_CUSTOM_ELEMENT(element) ||
2492         IS_INTERNAL_ELEMENT(element))
2493     {
2494       // internal values used in level editor
2495
2496       ei->access_type = 0;
2497       ei->access_layer = 0;
2498       ei->access_protected = 0;
2499       ei->walk_to_action = 0;
2500       ei->smash_targets = 0;
2501       ei->deadliness = 0;
2502
2503       ei->can_explode_by_fire = FALSE;
2504       ei->can_explode_smashed = FALSE;
2505       ei->can_explode_impact = FALSE;
2506
2507       ei->current_change_page = 0;
2508     }
2509
2510     if (IS_GROUP_ELEMENT(element) ||
2511         IS_INTERNAL_ELEMENT(element))
2512     {
2513       struct ElementGroupInfo *group;
2514
2515       // initialize memory for list of elements in group
2516       if (ei->group == NULL)
2517         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2518
2519       group = ei->group;
2520
2521       xx_group = *group;        // copy group data into temporary buffer
2522
2523       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2524
2525       *group = xx_group;
2526     }
2527
2528     if (IS_EMPTY_ELEMENT(element) ||
2529         IS_INTERNAL_ELEMENT(element))
2530     {
2531       xx_ei = *ei;              // copy element data into temporary buffer
2532
2533       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2534
2535       *ei = xx_ei;
2536     }
2537   }
2538
2539   clipboard_elements_initialized = TRUE;
2540 }
2541
2542 static void setLevelInfoToDefaults(struct LevelInfo *level,
2543                                    boolean level_info_only,
2544                                    boolean reset_file_status)
2545 {
2546   setLevelInfoToDefaults_Level(level);
2547
2548   if (!level_info_only)
2549     setLevelInfoToDefaults_Elements(level);
2550
2551   if (reset_file_status)
2552   {
2553     level->no_valid_file = FALSE;
2554     level->no_level_file = FALSE;
2555   }
2556
2557   level->changed = FALSE;
2558 }
2559
2560 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2561 {
2562   level_file_info->nr = 0;
2563   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2564   level_file_info->packed = FALSE;
2565
2566   setString(&level_file_info->basename, NULL);
2567   setString(&level_file_info->filename, NULL);
2568 }
2569
2570 int getMappedElement_SB(int, boolean);
2571
2572 static void ActivateLevelTemplate(void)
2573 {
2574   int x, y;
2575
2576   if (check_special_flags("load_xsb_to_ces"))
2577   {
2578     // fill smaller playfields with padding "beyond border wall" elements
2579     if (level.fieldx < level_template.fieldx ||
2580         level.fieldy < level_template.fieldy)
2581     {
2582       short field[level.fieldx][level.fieldy];
2583       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2584       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2585       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2586       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2587
2588       // copy old playfield (which is smaller than the visible area)
2589       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2590         field[x][y] = level.field[x][y];
2591
2592       // fill new, larger playfield with "beyond border wall" elements
2593       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2594         level.field[x][y] = getMappedElement_SB('_', TRUE);
2595
2596       // copy the old playfield to the middle of the new playfield
2597       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2598         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2599
2600       level.fieldx = new_fieldx;
2601       level.fieldy = new_fieldy;
2602     }
2603   }
2604
2605   // Currently there is no special action needed to activate the template
2606   // data, because 'element_info' property settings overwrite the original
2607   // level data, while all other variables do not change.
2608
2609   // Exception: 'from_level_template' elements in the original level playfield
2610   // are overwritten with the corresponding elements at the same position in
2611   // playfield from the level template.
2612
2613   for (x = 0; x < level.fieldx; x++)
2614     for (y = 0; y < level.fieldy; y++)
2615       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2616         level.field[x][y] = level_template.field[x][y];
2617
2618   if (check_special_flags("load_xsb_to_ces"))
2619   {
2620     struct LevelInfo level_backup = level;
2621
2622     // overwrite all individual level settings from template level settings
2623     level = level_template;
2624
2625     // restore level file info
2626     level.file_info = level_backup.file_info;
2627
2628     // restore playfield size
2629     level.fieldx = level_backup.fieldx;
2630     level.fieldy = level_backup.fieldy;
2631
2632     // restore playfield content
2633     for (x = 0; x < level.fieldx; x++)
2634       for (y = 0; y < level.fieldy; y++)
2635         level.field[x][y] = level_backup.field[x][y];
2636
2637     // restore name and author from individual level
2638     strcpy(level.name,   level_backup.name);
2639     strcpy(level.author, level_backup.author);
2640
2641     // restore flag "use_custom_template"
2642     level.use_custom_template = level_backup.use_custom_template;
2643   }
2644 }
2645
2646 static boolean checkForPackageFromBasename_BD(char *basename)
2647 {
2648   // check for native BD level file extensions
2649   if (!strSuffixLower(basename, ".bd") &&
2650       !strSuffixLower(basename, ".bdr") &&
2651       !strSuffixLower(basename, ".brc") &&
2652       !strSuffixLower(basename, ".gds"))
2653     return FALSE;
2654
2655   // check for standard single-level BD files (like "001.bd")
2656   if (strSuffixLower(basename, ".bd") &&
2657       strlen(basename) == 6 &&
2658       basename[0] >= '0' && basename[0] <= '9' &&
2659       basename[1] >= '0' && basename[1] <= '9' &&
2660       basename[2] >= '0' && basename[2] <= '9')
2661     return FALSE;
2662
2663   // this is a level package in native BD file format
2664   return TRUE;
2665 }
2666
2667 static char *getLevelFilenameFromBasename(char *basename)
2668 {
2669   static char *filename = NULL;
2670
2671   checked_free(filename);
2672
2673   filename = getPath2(getCurrentLevelDir(), basename);
2674
2675   return filename;
2676 }
2677
2678 static int getFileTypeFromBasename(char *basename)
2679 {
2680   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2681
2682   static char *filename = NULL;
2683   struct stat file_status;
2684
2685   // ---------- try to determine file type from filename ----------
2686
2687   // check for typical filename of a Supaplex level package file
2688   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2689     return LEVEL_FILE_TYPE_SP;
2690
2691   // check for typical filename of a Diamond Caves II level package file
2692   if (strSuffixLower(basename, ".dc") ||
2693       strSuffixLower(basename, ".dc2"))
2694     return LEVEL_FILE_TYPE_DC;
2695
2696   // check for typical filename of a Sokoban level package file
2697   if (strSuffixLower(basename, ".xsb") &&
2698       strchr(basename, '%') == NULL)
2699     return LEVEL_FILE_TYPE_SB;
2700
2701   // check for typical filename of a Boulder Dash (GDash) level package file
2702   if (checkForPackageFromBasename_BD(basename))
2703     return LEVEL_FILE_TYPE_BD;
2704
2705   // ---------- try to determine file type from filesize ----------
2706
2707   checked_free(filename);
2708   filename = getPath2(getCurrentLevelDir(), basename);
2709
2710   if (stat(filename, &file_status) == 0)
2711   {
2712     // check for typical filesize of a Supaplex level package file
2713     if (file_status.st_size == 170496)
2714       return LEVEL_FILE_TYPE_SP;
2715   }
2716
2717   return LEVEL_FILE_TYPE_UNKNOWN;
2718 }
2719
2720 static int getFileTypeFromMagicBytes(char *filename, int type)
2721 {
2722   File *file;
2723
2724   if ((file = openFile(filename, MODE_READ)))
2725   {
2726     char chunk_name[CHUNK_ID_LEN + 1];
2727
2728     getFileChunkBE(file, chunk_name, NULL);
2729
2730     if (strEqual(chunk_name, "MMII") ||
2731         strEqual(chunk_name, "MIRR"))
2732       type = LEVEL_FILE_TYPE_MM;
2733
2734     closeFile(file);
2735   }
2736
2737   return type;
2738 }
2739
2740 static boolean checkForPackageFromBasename(char *basename)
2741 {
2742   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2743   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2744
2745   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2746 }
2747
2748 static char *getSingleLevelBasenameExt(int nr, char *extension)
2749 {
2750   static char basename[MAX_FILENAME_LEN];
2751
2752   if (nr < 0)
2753     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2754   else
2755     sprintf(basename, "%03d.%s", nr, extension);
2756
2757   return basename;
2758 }
2759
2760 static char *getSingleLevelBasename(int nr)
2761 {
2762   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2763 }
2764
2765 static char *getPackedLevelBasename(int type)
2766 {
2767   static char basename[MAX_FILENAME_LEN];
2768   char *directory = getCurrentLevelDir();
2769   Directory *dir;
2770   DirectoryEntry *dir_entry;
2771
2772   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2773
2774   if ((dir = openDirectory(directory)) == NULL)
2775   {
2776     Warn("cannot read current level directory '%s'", directory);
2777
2778     return basename;
2779   }
2780
2781   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2782   {
2783     char *entry_basename = dir_entry->basename;
2784     int entry_type = getFileTypeFromBasename(entry_basename);
2785
2786     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2787     {
2788       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2789           type == entry_type)
2790       {
2791         strcpy(basename, entry_basename);
2792
2793         break;
2794       }
2795     }
2796   }
2797
2798   closeDirectory(dir);
2799
2800   return basename;
2801 }
2802
2803 static char *getSingleLevelFilename(int nr)
2804 {
2805   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2806 }
2807
2808 #if ENABLE_UNUSED_CODE
2809 static char *getPackedLevelFilename(int type)
2810 {
2811   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2812 }
2813 #endif
2814
2815 char *getDefaultLevelFilename(int nr)
2816 {
2817   return getSingleLevelFilename(nr);
2818 }
2819
2820 #if ENABLE_UNUSED_CODE
2821 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2822                                                  int type)
2823 {
2824   lfi->type = type;
2825   lfi->packed = FALSE;
2826
2827   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2828   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2829 }
2830 #endif
2831
2832 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2833                                                  int type, char *format, ...)
2834 {
2835   static char basename[MAX_FILENAME_LEN];
2836   va_list ap;
2837
2838   va_start(ap, format);
2839   vsprintf(basename, format, ap);
2840   va_end(ap);
2841
2842   lfi->type = type;
2843   lfi->packed = FALSE;
2844
2845   setString(&lfi->basename, basename);
2846   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2847 }
2848
2849 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2850                                                  int type)
2851 {
2852   lfi->type = type;
2853   lfi->packed = TRUE;
2854
2855   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2856   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2857 }
2858
2859 static int getFiletypeFromID(char *filetype_id)
2860 {
2861   char *filetype_id_lower;
2862   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2863   int i;
2864
2865   if (filetype_id == NULL)
2866     return LEVEL_FILE_TYPE_UNKNOWN;
2867
2868   filetype_id_lower = getStringToLower(filetype_id);
2869
2870   for (i = 0; filetype_id_list[i].id != NULL; i++)
2871   {
2872     char *id_lower = getStringToLower(filetype_id_list[i].id);
2873     
2874     if (strEqual(filetype_id_lower, id_lower))
2875       filetype = filetype_id_list[i].filetype;
2876
2877     free(id_lower);
2878
2879     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2880       break;
2881   }
2882
2883   free(filetype_id_lower);
2884
2885   return filetype;
2886 }
2887
2888 char *getLocalLevelTemplateFilename(void)
2889 {
2890   return getDefaultLevelFilename(-1);
2891 }
2892
2893 char *getGlobalLevelTemplateFilename(void)
2894 {
2895   // global variable "leveldir_current" must be modified in the loop below
2896   LevelDirTree *leveldir_current_last = leveldir_current;
2897   char *filename = NULL;
2898
2899   // check for template level in path from current to topmost tree node
2900
2901   while (leveldir_current != NULL)
2902   {
2903     filename = getDefaultLevelFilename(-1);
2904
2905     if (fileExists(filename))
2906       break;
2907
2908     leveldir_current = leveldir_current->node_parent;
2909   }
2910
2911   // restore global variable "leveldir_current" modified in above loop
2912   leveldir_current = leveldir_current_last;
2913
2914   return filename;
2915 }
2916
2917 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2918 {
2919   int nr = lfi->nr;
2920
2921   // special case: level number is negative => check for level template file
2922   if (nr < 0)
2923   {
2924     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2925                                          getSingleLevelBasename(-1));
2926
2927     // replace local level template filename with global template filename
2928     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2929
2930     // no fallback if template file not existing
2931     return;
2932   }
2933
2934   // special case: check for file name/pattern specified in "levelinfo.conf"
2935   if (leveldir_current->level_filename != NULL)
2936   {
2937     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2938
2939     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2940                                          leveldir_current->level_filename, nr);
2941
2942     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2943
2944     if (fileExists(lfi->filename))
2945       return;
2946   }
2947   else if (leveldir_current->level_filetype != NULL)
2948   {
2949     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2950
2951     // check for specified native level file with standard file name
2952     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2953                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2954     if (fileExists(lfi->filename))
2955       return;
2956   }
2957
2958   // check for native Rocks'n'Diamonds level file
2959   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2960                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2961   if (fileExists(lfi->filename))
2962     return;
2963
2964   // check for native Boulder Dash level file
2965   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2966   if (fileExists(lfi->filename))
2967     return;
2968
2969   // check for Emerald Mine level file (V1)
2970   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2971                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2972   if (fileExists(lfi->filename))
2973     return;
2974   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2975                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2976   if (fileExists(lfi->filename))
2977     return;
2978
2979   // check for Emerald Mine level file (V2 to V5)
2980   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2981   if (fileExists(lfi->filename))
2982     return;
2983
2984   // check for Emerald Mine level file (V6 / single mode)
2985   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2986   if (fileExists(lfi->filename))
2987     return;
2988   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2989   if (fileExists(lfi->filename))
2990     return;
2991
2992   // check for Emerald Mine level file (V6 / teamwork mode)
2993   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2994   if (fileExists(lfi->filename))
2995     return;
2996   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2997   if (fileExists(lfi->filename))
2998     return;
2999
3000   // check for various packed level file formats
3001   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
3002   if (fileExists(lfi->filename))
3003     return;
3004
3005   // no known level file found -- use default values (and fail later)
3006   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
3007                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
3008 }
3009
3010 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
3011 {
3012   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
3013     lfi->type = getFileTypeFromBasename(lfi->basename);
3014
3015   if (lfi->type == LEVEL_FILE_TYPE_RND)
3016     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
3017 }
3018
3019 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
3020 {
3021   // always start with reliable default values
3022   setFileInfoToDefaults(level_file_info);
3023
3024   level_file_info->nr = nr;     // set requested level number
3025
3026   determineLevelFileInfo_Filename(level_file_info);
3027   determineLevelFileInfo_Filetype(level_file_info);
3028 }
3029
3030 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
3031                               struct LevelFileInfo *lfi_to)
3032 {
3033   lfi_to->nr     = lfi_from->nr;
3034   lfi_to->type   = lfi_from->type;
3035   lfi_to->packed = lfi_from->packed;
3036
3037   setString(&lfi_to->basename, lfi_from->basename);
3038   setString(&lfi_to->filename, lfi_from->filename);
3039 }
3040
3041 // ----------------------------------------------------------------------------
3042 // functions for loading R'n'D level
3043 // ----------------------------------------------------------------------------
3044
3045 int getMappedElement(int element)
3046 {
3047   // remap some (historic, now obsolete) elements
3048
3049   switch (element)
3050   {
3051     case EL_PLAYER_OBSOLETE:
3052       element = EL_PLAYER_1;
3053       break;
3054
3055     case EL_KEY_OBSOLETE:
3056       element = EL_KEY_1;
3057       break;
3058
3059     case EL_EM_KEY_1_FILE_OBSOLETE:
3060       element = EL_EM_KEY_1;
3061       break;
3062
3063     case EL_EM_KEY_2_FILE_OBSOLETE:
3064       element = EL_EM_KEY_2;
3065       break;
3066
3067     case EL_EM_KEY_3_FILE_OBSOLETE:
3068       element = EL_EM_KEY_3;
3069       break;
3070
3071     case EL_EM_KEY_4_FILE_OBSOLETE:
3072       element = EL_EM_KEY_4;
3073       break;
3074
3075     case EL_ENVELOPE_OBSOLETE:
3076       element = EL_ENVELOPE_1;
3077       break;
3078
3079     case EL_SP_EMPTY:
3080       element = EL_EMPTY;
3081       break;
3082
3083     default:
3084       if (element >= NUM_FILE_ELEMENTS)
3085       {
3086         Warn("invalid level element %d", element);
3087
3088         element = EL_UNKNOWN;
3089       }
3090       break;
3091   }
3092
3093   return element;
3094 }
3095
3096 static int getMappedElementByVersion(int element, int game_version)
3097 {
3098   // remap some elements due to certain game version
3099
3100   if (game_version <= VERSION_IDENT(2,2,0,0))
3101   {
3102     // map game font elements
3103     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3104                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3105                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3106                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3107   }
3108
3109   if (game_version < VERSION_IDENT(3,0,0,0))
3110   {
3111     // map Supaplex gravity tube elements
3112     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3113                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3114                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3115                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3116                element);
3117   }
3118
3119   return element;
3120 }
3121
3122 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3123 {
3124   level->file_version = getFileVersion(file);
3125   level->game_version = getFileVersion(file);
3126
3127   return chunk_size;
3128 }
3129
3130 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3131 {
3132   level->creation_date.year  = getFile16BitBE(file);
3133   level->creation_date.month = getFile8Bit(file);
3134   level->creation_date.day   = getFile8Bit(file);
3135
3136   level->creation_date.src   = DATE_SRC_LEVELFILE;
3137
3138   return chunk_size;
3139 }
3140
3141 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3142 {
3143   int initial_player_stepsize;
3144   int initial_player_gravity;
3145   int i, x, y;
3146
3147   level->fieldx = getFile8Bit(file);
3148   level->fieldy = getFile8Bit(file);
3149
3150   level->time           = getFile16BitBE(file);
3151   level->gems_needed    = getFile16BitBE(file);
3152
3153   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3154     level->name[i] = getFile8Bit(file);
3155   level->name[MAX_LEVEL_NAME_LEN] = 0;
3156
3157   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3158     level->score[i] = getFile8Bit(file);
3159
3160   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3161   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3162     for (y = 0; y < 3; y++)
3163       for (x = 0; x < 3; x++)
3164         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3165
3166   level->amoeba_speed           = getFile8Bit(file);
3167   level->time_magic_wall        = getFile8Bit(file);
3168   level->time_wheel             = getFile8Bit(file);
3169   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3170
3171   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3172                                    STEPSIZE_NORMAL);
3173
3174   for (i = 0; i < MAX_PLAYERS; i++)
3175     level->initial_player_stepsize[i] = initial_player_stepsize;
3176
3177   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3178
3179   for (i = 0; i < MAX_PLAYERS; i++)
3180     level->initial_player_gravity[i] = initial_player_gravity;
3181
3182   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3183   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3184
3185   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3186
3187   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3188   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3189   level->can_move_into_acid_bits = getFile32BitBE(file);
3190   level->dont_collide_with_bits = getFile8Bit(file);
3191
3192   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3193   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3194
3195   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3196   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3197   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3198
3199   level->game_engine_type       = getFile8Bit(file);
3200
3201   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3202
3203   return chunk_size;
3204 }
3205
3206 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3207 {
3208   int i;
3209
3210   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3211     level->name[i] = getFile8Bit(file);
3212   level->name[MAX_LEVEL_NAME_LEN] = 0;
3213
3214   return chunk_size;
3215 }
3216
3217 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3218 {
3219   int i;
3220
3221   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3222     level->author[i] = getFile8Bit(file);
3223   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3224
3225   return chunk_size;
3226 }
3227
3228 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3229 {
3230   int x, y;
3231   int chunk_size_expected = level->fieldx * level->fieldy;
3232
3233   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3234      stored with 16-bit encoding (and should be twice as big then).
3235      Even worse, playfield data was stored 16-bit when only yamyam content
3236      contained 16-bit elements and vice versa. */
3237
3238   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3239     chunk_size_expected *= 2;
3240
3241   if (chunk_size_expected != chunk_size)
3242   {
3243     ReadUnusedBytesFromFile(file, chunk_size);
3244     return chunk_size_expected;
3245   }
3246
3247   for (y = 0; y < level->fieldy; y++)
3248     for (x = 0; x < level->fieldx; x++)
3249       level->field[x][y] =
3250         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3251                          getFile8Bit(file));
3252   return chunk_size;
3253 }
3254
3255 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3256 {
3257   int i, x, y;
3258   int header_size = 4;
3259   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3260   int chunk_size_expected = header_size + content_size;
3261
3262   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3263      stored with 16-bit encoding (and should be twice as big then).
3264      Even worse, playfield data was stored 16-bit when only yamyam content
3265      contained 16-bit elements and vice versa. */
3266
3267   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3268     chunk_size_expected += content_size;
3269
3270   if (chunk_size_expected != chunk_size)
3271   {
3272     ReadUnusedBytesFromFile(file, chunk_size);
3273     return chunk_size_expected;
3274   }
3275
3276   getFile8Bit(file);
3277   level->num_yamyam_contents = getFile8Bit(file);
3278   getFile8Bit(file);
3279   getFile8Bit(file);
3280
3281   // correct invalid number of content fields -- should never happen
3282   if (level->num_yamyam_contents < 1 ||
3283       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3284     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3285
3286   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3287     for (y = 0; y < 3; y++)
3288       for (x = 0; x < 3; x++)
3289         level->yamyam_content[i].e[x][y] =
3290           getMappedElement(level->encoding_16bit_field ?
3291                            getFile16BitBE(file) : getFile8Bit(file));
3292   return chunk_size;
3293 }
3294
3295 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3296 {
3297   int i, x, y;
3298   int element;
3299   int num_contents;
3300   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3301
3302   element = getMappedElement(getFile16BitBE(file));
3303   num_contents = getFile8Bit(file);
3304
3305   getFile8Bit(file);    // content x size (unused)
3306   getFile8Bit(file);    // content y size (unused)
3307
3308   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3309
3310   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3311     for (y = 0; y < 3; y++)
3312       for (x = 0; x < 3; x++)
3313         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3314
3315   // correct invalid number of content fields -- should never happen
3316   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3317     num_contents = STD_ELEMENT_CONTENTS;
3318
3319   if (element == EL_YAMYAM)
3320   {
3321     level->num_yamyam_contents = num_contents;
3322
3323     for (i = 0; i < num_contents; i++)
3324       for (y = 0; y < 3; y++)
3325         for (x = 0; x < 3; x++)
3326           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3327   }
3328   else if (element == EL_BD_AMOEBA)
3329   {
3330     level->amoeba_content = content_array[0][0][0];
3331   }
3332   else
3333   {
3334     Warn("cannot load content for element '%d'", element);
3335   }
3336
3337   return chunk_size;
3338 }
3339
3340 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3341 {
3342   int i;
3343   int element;
3344   int envelope_nr;
3345   int envelope_len;
3346   int chunk_size_expected;
3347
3348   element = getMappedElement(getFile16BitBE(file));
3349   if (!IS_ENVELOPE(element))
3350     element = EL_ENVELOPE_1;
3351
3352   envelope_nr = element - EL_ENVELOPE_1;
3353
3354   envelope_len = getFile16BitBE(file);
3355
3356   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3357   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3358
3359   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3360
3361   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3362   if (chunk_size_expected != chunk_size)
3363   {
3364     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3365     return chunk_size_expected;
3366   }
3367
3368   for (i = 0; i < envelope_len; i++)
3369     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3370
3371   return chunk_size;
3372 }
3373
3374 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3375 {
3376   int num_changed_custom_elements = getFile16BitBE(file);
3377   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3378   int i;
3379
3380   if (chunk_size_expected != chunk_size)
3381   {
3382     ReadUnusedBytesFromFile(file, chunk_size - 2);
3383     return chunk_size_expected;
3384   }
3385
3386   for (i = 0; i < num_changed_custom_elements; i++)
3387   {
3388     int element = getMappedElement(getFile16BitBE(file));
3389     int properties = getFile32BitBE(file);
3390
3391     if (IS_CUSTOM_ELEMENT(element))
3392       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3393     else
3394       Warn("invalid custom element number %d", element);
3395
3396     // older game versions that wrote level files with CUS1 chunks used
3397     // different default push delay values (not yet stored in level file)
3398     element_info[element].push_delay_fixed = 2;
3399     element_info[element].push_delay_random = 8;
3400   }
3401
3402   level->file_has_custom_elements = TRUE;
3403
3404   return chunk_size;
3405 }
3406
3407 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3408 {
3409   int num_changed_custom_elements = getFile16BitBE(file);
3410   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3411   int i;
3412
3413   if (chunk_size_expected != chunk_size)
3414   {
3415     ReadUnusedBytesFromFile(file, chunk_size - 2);
3416     return chunk_size_expected;
3417   }
3418
3419   for (i = 0; i < num_changed_custom_elements; i++)
3420   {
3421     int element = getMappedElement(getFile16BitBE(file));
3422     int custom_target_element = getMappedElement(getFile16BitBE(file));
3423
3424     if (IS_CUSTOM_ELEMENT(element))
3425       element_info[element].change->target_element = custom_target_element;
3426     else
3427       Warn("invalid custom element number %d", element);
3428   }
3429
3430   level->file_has_custom_elements = TRUE;
3431
3432   return chunk_size;
3433 }
3434
3435 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3436 {
3437   int num_changed_custom_elements = getFile16BitBE(file);
3438   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3439   int i, j, x, y;
3440
3441   if (chunk_size_expected != chunk_size)
3442   {
3443     ReadUnusedBytesFromFile(file, chunk_size - 2);
3444     return chunk_size_expected;
3445   }
3446
3447   for (i = 0; i < num_changed_custom_elements; i++)
3448   {
3449     int element = getMappedElement(getFile16BitBE(file));
3450     struct ElementInfo *ei = &element_info[element];
3451     unsigned int event_bits;
3452
3453     if (!IS_CUSTOM_ELEMENT(element))
3454     {
3455       Warn("invalid custom element number %d", element);
3456
3457       element = EL_INTERNAL_DUMMY;
3458     }
3459
3460     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3461       ei->description[j] = getFile8Bit(file);
3462     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3463
3464     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3465
3466     // some free bytes for future properties and padding
3467     ReadUnusedBytesFromFile(file, 7);
3468
3469     ei->use_gfx_element = getFile8Bit(file);
3470     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3471
3472     ei->collect_score_initial = getFile8Bit(file);
3473     ei->collect_count_initial = getFile8Bit(file);
3474
3475     ei->push_delay_fixed = getFile16BitBE(file);
3476     ei->push_delay_random = getFile16BitBE(file);
3477     ei->move_delay_fixed = getFile16BitBE(file);
3478     ei->move_delay_random = getFile16BitBE(file);
3479
3480     ei->move_pattern = getFile16BitBE(file);
3481     ei->move_direction_initial = getFile8Bit(file);
3482     ei->move_stepsize = getFile8Bit(file);
3483
3484     for (y = 0; y < 3; y++)
3485       for (x = 0; x < 3; x++)
3486         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3487
3488     // bits 0 - 31 of "has_event[]"
3489     event_bits = getFile32BitBE(file);
3490     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3491       if (event_bits & (1u << j))
3492         ei->change->has_event[j] = TRUE;
3493
3494     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3495
3496     ei->change->delay_fixed = getFile16BitBE(file);
3497     ei->change->delay_random = getFile16BitBE(file);
3498     ei->change->delay_frames = getFile16BitBE(file);
3499
3500     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3501
3502     ei->change->explode = getFile8Bit(file);
3503     ei->change->use_target_content = getFile8Bit(file);
3504     ei->change->only_if_complete = getFile8Bit(file);
3505     ei->change->use_random_replace = getFile8Bit(file);
3506
3507     ei->change->random_percentage = getFile8Bit(file);
3508     ei->change->replace_when = getFile8Bit(file);
3509
3510     for (y = 0; y < 3; y++)
3511       for (x = 0; x < 3; x++)
3512         ei->change->target_content.e[x][y] =
3513           getMappedElement(getFile16BitBE(file));
3514
3515     ei->slippery_type = getFile8Bit(file);
3516
3517     // some free bytes for future properties and padding
3518     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3519
3520     // mark that this custom element has been modified
3521     ei->modified_settings = TRUE;
3522   }
3523
3524   level->file_has_custom_elements = TRUE;
3525
3526   return chunk_size;
3527 }
3528
3529 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3530 {
3531   struct ElementInfo *ei;
3532   int chunk_size_expected;
3533   int element;
3534   int i, j, x, y;
3535
3536   // ---------- custom element base property values (96 bytes) ----------------
3537
3538   element = getMappedElement(getFile16BitBE(file));
3539
3540   if (!IS_CUSTOM_ELEMENT(element))
3541   {
3542     Warn("invalid custom element number %d", element);
3543
3544     ReadUnusedBytesFromFile(file, chunk_size - 2);
3545
3546     return chunk_size;
3547   }
3548
3549   ei = &element_info[element];
3550
3551   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3552     ei->description[i] = getFile8Bit(file);
3553   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3554
3555   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3556
3557   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3558
3559   ei->num_change_pages = getFile8Bit(file);
3560
3561   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3562   if (chunk_size_expected != chunk_size)
3563   {
3564     ReadUnusedBytesFromFile(file, chunk_size - 43);
3565     return chunk_size_expected;
3566   }
3567
3568   ei->ce_value_fixed_initial = getFile16BitBE(file);
3569   ei->ce_value_random_initial = getFile16BitBE(file);
3570   ei->use_last_ce_value = getFile8Bit(file);
3571
3572   ei->use_gfx_element = getFile8Bit(file);
3573   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3574
3575   ei->collect_score_initial = getFile8Bit(file);
3576   ei->collect_count_initial = getFile8Bit(file);
3577
3578   ei->drop_delay_fixed = getFile8Bit(file);
3579   ei->push_delay_fixed = getFile8Bit(file);
3580   ei->drop_delay_random = getFile8Bit(file);
3581   ei->push_delay_random = getFile8Bit(file);
3582   ei->move_delay_fixed = getFile16BitBE(file);
3583   ei->move_delay_random = getFile16BitBE(file);
3584
3585   // bits 0 - 15 of "move_pattern" ...
3586   ei->move_pattern = getFile16BitBE(file);
3587   ei->move_direction_initial = getFile8Bit(file);
3588   ei->move_stepsize = getFile8Bit(file);
3589
3590   ei->slippery_type = getFile8Bit(file);
3591
3592   for (y = 0; y < 3; y++)
3593     for (x = 0; x < 3; x++)
3594       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3595
3596   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3597   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3598   ei->move_leave_type = getFile8Bit(file);
3599
3600   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3601   ei->move_pattern |= (getFile16BitBE(file) << 16);
3602
3603   ei->access_direction = getFile8Bit(file);
3604
3605   ei->explosion_delay = getFile8Bit(file);
3606   ei->ignition_delay = getFile8Bit(file);
3607   ei->explosion_type = getFile8Bit(file);
3608
3609   // some free bytes for future custom property values and padding
3610   ReadUnusedBytesFromFile(file, 1);
3611
3612   // ---------- change page property values (48 bytes) ------------------------
3613
3614   setElementChangePages(ei, ei->num_change_pages);
3615
3616   for (i = 0; i < ei->num_change_pages; i++)
3617   {
3618     struct ElementChangeInfo *change = &ei->change_page[i];
3619     unsigned int event_bits;
3620
3621     // always start with reliable default values
3622     setElementChangeInfoToDefaults(change);
3623
3624     // bits 0 - 31 of "has_event[]" ...
3625     event_bits = getFile32BitBE(file);
3626     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3627       if (event_bits & (1u << j))
3628         change->has_event[j] = TRUE;
3629
3630     change->target_element = getMappedElement(getFile16BitBE(file));
3631
3632     change->delay_fixed = getFile16BitBE(file);
3633     change->delay_random = getFile16BitBE(file);
3634     change->delay_frames = getFile16BitBE(file);
3635
3636     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3637
3638     change->explode = getFile8Bit(file);
3639     change->use_target_content = getFile8Bit(file);
3640     change->only_if_complete = getFile8Bit(file);
3641     change->use_random_replace = getFile8Bit(file);
3642
3643     change->random_percentage = getFile8Bit(file);
3644     change->replace_when = getFile8Bit(file);
3645
3646     for (y = 0; y < 3; y++)
3647       for (x = 0; x < 3; x++)
3648         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3649
3650     change->can_change = getFile8Bit(file);
3651
3652     change->trigger_side = getFile8Bit(file);
3653
3654     change->trigger_player = getFile8Bit(file);
3655     change->trigger_page = getFile8Bit(file);
3656
3657     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3658                             CH_PAGE_ANY : (1 << change->trigger_page));
3659
3660     change->has_action = getFile8Bit(file);
3661     change->action_type = getFile8Bit(file);
3662     change->action_mode = getFile8Bit(file);
3663     change->action_arg = getFile16BitBE(file);
3664
3665     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3666     event_bits = getFile8Bit(file);
3667     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3668       if (event_bits & (1u << (j - 32)))
3669         change->has_event[j] = TRUE;
3670   }
3671
3672   // mark this custom element as modified
3673   ei->modified_settings = TRUE;
3674
3675   level->file_has_custom_elements = TRUE;
3676
3677   return chunk_size;
3678 }
3679
3680 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3681 {
3682   struct ElementInfo *ei;
3683   struct ElementGroupInfo *group;
3684   int element;
3685   int i;
3686
3687   element = getMappedElement(getFile16BitBE(file));
3688
3689   if (!IS_GROUP_ELEMENT(element))
3690   {
3691     Warn("invalid group element number %d", element);
3692
3693     ReadUnusedBytesFromFile(file, chunk_size - 2);
3694
3695     return chunk_size;
3696   }
3697
3698   ei = &element_info[element];
3699
3700   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3701     ei->description[i] = getFile8Bit(file);
3702   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3703
3704   group = element_info[element].group;
3705
3706   group->num_elements = getFile8Bit(file);
3707
3708   ei->use_gfx_element = getFile8Bit(file);
3709   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3710
3711   group->choice_mode = getFile8Bit(file);
3712
3713   // some free bytes for future values and padding
3714   ReadUnusedBytesFromFile(file, 3);
3715
3716   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3717     group->element[i] = getMappedElement(getFile16BitBE(file));
3718
3719   // mark this group element as modified
3720   element_info[element].modified_settings = TRUE;
3721
3722   level->file_has_custom_elements = TRUE;
3723
3724   return chunk_size;
3725 }
3726
3727 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3728                                 int element, int real_element)
3729 {
3730   int micro_chunk_size = 0;
3731   int conf_type = getFile8Bit(file);
3732   int byte_mask = conf_type & CONF_MASK_BYTES;
3733   boolean element_found = FALSE;
3734   int i;
3735
3736   micro_chunk_size += 1;
3737
3738   if (byte_mask == CONF_MASK_MULTI_BYTES)
3739   {
3740     int num_bytes = getFile16BitBE(file);
3741     byte *buffer = checked_malloc(num_bytes);
3742
3743     ReadBytesFromFile(file, buffer, num_bytes);
3744
3745     for (i = 0; conf[i].data_type != -1; i++)
3746     {
3747       if (conf[i].element == element &&
3748           conf[i].conf_type == conf_type)
3749       {
3750         int data_type = conf[i].data_type;
3751         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3752         int max_num_entities = conf[i].max_num_entities;
3753
3754         if (num_entities > max_num_entities)
3755         {
3756           Warn("truncating number of entities for element %d from %d to %d",
3757                element, num_entities, max_num_entities);
3758
3759           num_entities = max_num_entities;
3760         }
3761
3762         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3763                                   data_type == TYPE_CONTENT_LIST))
3764         {
3765           // for element and content lists, zero entities are not allowed
3766           Warn("found empty list of entities for element %d", element);
3767
3768           // do not set "num_entities" here to prevent reading behind buffer
3769
3770           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3771         }
3772         else
3773         {
3774           *(int *)(conf[i].num_entities) = num_entities;
3775         }
3776
3777         element_found = TRUE;
3778
3779         if (data_type == TYPE_STRING)
3780         {
3781           char *string = (char *)(conf[i].value);
3782           int j;
3783
3784           for (j = 0; j < max_num_entities; j++)
3785             string[j] = (j < num_entities ? buffer[j] : '\0');
3786         }
3787         else if (data_type == TYPE_ELEMENT_LIST)
3788         {
3789           int *element_array = (int *)(conf[i].value);
3790           int j;
3791
3792           for (j = 0; j < num_entities; j++)
3793             element_array[j] =
3794               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3795         }
3796         else if (data_type == TYPE_CONTENT_LIST)
3797         {
3798           struct Content *content= (struct Content *)(conf[i].value);
3799           int c, x, y;
3800
3801           for (c = 0; c < num_entities; c++)
3802             for (y = 0; y < 3; y++)
3803               for (x = 0; x < 3; x++)
3804                 content[c].e[x][y] =
3805                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3806         }
3807         else
3808           element_found = FALSE;
3809
3810         break;
3811       }
3812     }
3813
3814     checked_free(buffer);
3815
3816     micro_chunk_size += 2 + num_bytes;
3817   }
3818   else          // constant size configuration data (1, 2 or 4 bytes)
3819   {
3820     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3821                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3822                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3823
3824     for (i = 0; conf[i].data_type != -1; i++)
3825     {
3826       if (conf[i].element == element &&
3827           conf[i].conf_type == conf_type)
3828       {
3829         int data_type = conf[i].data_type;
3830
3831         if (data_type == TYPE_ELEMENT)
3832           value = getMappedElement(value);
3833
3834         if (data_type == TYPE_BOOLEAN)
3835           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3836         else
3837           *(int *)    (conf[i].value) = value;
3838
3839         element_found = TRUE;
3840
3841         break;
3842       }
3843     }
3844
3845     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3846   }
3847
3848   if (!element_found)
3849   {
3850     char *error_conf_chunk_bytes =
3851       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3852        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3853        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3854     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3855     int error_element = real_element;
3856
3857     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3858          error_conf_chunk_bytes, error_conf_chunk_token,
3859          error_element, EL_NAME(error_element));
3860   }
3861
3862   return micro_chunk_size;
3863 }
3864
3865 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3866 {
3867   int real_chunk_size = 0;
3868
3869   li = *level;          // copy level data into temporary buffer
3870
3871   while (!checkEndOfFile(file))
3872   {
3873     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3874
3875     if (real_chunk_size >= chunk_size)
3876       break;
3877   }
3878
3879   *level = li;          // copy temporary buffer back to level data
3880
3881   return real_chunk_size;
3882 }
3883
3884 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3885 {
3886   int real_chunk_size = 0;
3887
3888   li = *level;          // copy level data into temporary buffer
3889
3890   while (!checkEndOfFile(file))
3891   {
3892     int element = getMappedElement(getFile16BitBE(file));
3893
3894     real_chunk_size += 2;
3895     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3896                                             element, element);
3897     if (real_chunk_size >= chunk_size)
3898       break;
3899   }
3900
3901   *level = li;          // copy temporary buffer back to level data
3902
3903   return real_chunk_size;
3904 }
3905
3906 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3907 {
3908   int real_chunk_size = 0;
3909
3910   li = *level;          // copy level data into temporary buffer
3911
3912   while (!checkEndOfFile(file))
3913   {
3914     int element = getMappedElement(getFile16BitBE(file));
3915
3916     real_chunk_size += 2;
3917     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3918                                             element, element);
3919     if (real_chunk_size >= chunk_size)
3920       break;
3921   }
3922
3923   *level = li;          // copy temporary buffer back to level data
3924
3925   return real_chunk_size;
3926 }
3927
3928 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3929 {
3930   int element = getMappedElement(getFile16BitBE(file));
3931   int envelope_nr = element - EL_ENVELOPE_1;
3932   int real_chunk_size = 2;
3933
3934   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3935
3936   while (!checkEndOfFile(file))
3937   {
3938     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3939                                             -1, element);
3940
3941     if (real_chunk_size >= chunk_size)
3942       break;
3943   }
3944
3945   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3946
3947   return real_chunk_size;
3948 }
3949
3950 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3951 {
3952   int element = getMappedElement(getFile16BitBE(file));
3953   int real_chunk_size = 2;
3954   struct ElementInfo *ei = &element_info[element];
3955   int i;
3956
3957   xx_ei = *ei;          // copy element data into temporary buffer
3958
3959   xx_ei.num_change_pages = -1;
3960
3961   while (!checkEndOfFile(file))
3962   {
3963     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3964                                             -1, element);
3965     if (xx_ei.num_change_pages != -1)
3966       break;
3967
3968     if (real_chunk_size >= chunk_size)
3969       break;
3970   }
3971
3972   *ei = xx_ei;
3973
3974   if (ei->num_change_pages == -1)
3975   {
3976     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3977          EL_NAME(element));
3978
3979     ei->num_change_pages = 1;
3980
3981     setElementChangePages(ei, 1);
3982     setElementChangeInfoToDefaults(ei->change);
3983
3984     return real_chunk_size;
3985   }
3986
3987   // initialize number of change pages stored for this custom element
3988   setElementChangePages(ei, ei->num_change_pages);
3989   for (i = 0; i < ei->num_change_pages; i++)
3990     setElementChangeInfoToDefaults(&ei->change_page[i]);
3991
3992   // start with reading properties for the first change page
3993   xx_current_change_page = 0;
3994
3995   while (!checkEndOfFile(file))
3996   {
3997     // level file might contain invalid change page number
3998     if (xx_current_change_page >= ei->num_change_pages)
3999       break;
4000
4001     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
4002
4003     xx_change = *change;        // copy change data into temporary buffer
4004
4005     resetEventBits();           // reset bits; change page might have changed
4006
4007     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
4008                                             -1, element);
4009
4010     *change = xx_change;
4011
4012     setEventFlagsFromEventBits(change);
4013
4014     if (real_chunk_size >= chunk_size)
4015       break;
4016   }
4017
4018   level->file_has_custom_elements = TRUE;
4019
4020   return real_chunk_size;
4021 }
4022
4023 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
4024 {
4025   int element = getMappedElement(getFile16BitBE(file));
4026   int real_chunk_size = 2;
4027   struct ElementInfo *ei = &element_info[element];
4028   struct ElementGroupInfo *group = ei->group;
4029
4030   if (group == NULL)
4031     return -1;
4032
4033   xx_ei = *ei;          // copy element data into temporary buffer
4034   xx_group = *group;    // copy group data into temporary buffer
4035
4036   while (!checkEndOfFile(file))
4037   {
4038     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
4039                                             -1, element);
4040
4041     if (real_chunk_size >= chunk_size)
4042       break;
4043   }
4044
4045   *ei = xx_ei;
4046   *group = xx_group;
4047
4048   level->file_has_custom_elements = TRUE;
4049
4050   return real_chunk_size;
4051 }
4052
4053 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4054 {
4055   int element = getMappedElement(getFile16BitBE(file));
4056   int real_chunk_size = 2;
4057   struct ElementInfo *ei = &element_info[element];
4058
4059   xx_ei = *ei;          // copy element data into temporary buffer
4060
4061   while (!checkEndOfFile(file))
4062   {
4063     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4064                                             -1, element);
4065
4066     if (real_chunk_size >= chunk_size)
4067       break;
4068   }
4069
4070   *ei = xx_ei;
4071
4072   level->file_has_custom_elements = TRUE;
4073
4074   return real_chunk_size;
4075 }
4076
4077 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4078                                       struct LevelFileInfo *level_file_info,
4079                                       boolean level_info_only)
4080 {
4081   char *filename = level_file_info->filename;
4082   char cookie[MAX_LINE_LEN];
4083   char chunk_name[CHUNK_ID_LEN + 1];
4084   int chunk_size;
4085   File *file;
4086
4087   if (!(file = openFile(filename, MODE_READ)))
4088   {
4089     level->no_valid_file = TRUE;
4090     level->no_level_file = TRUE;
4091
4092     if (level_info_only)
4093       return;
4094
4095     Warn("cannot read level '%s' -- using empty level", filename);
4096
4097     if (!setup.editor.use_template_for_new_levels)
4098       return;
4099
4100     // if level file not found, try to initialize level data from template
4101     filename = getGlobalLevelTemplateFilename();
4102
4103     if (!(file = openFile(filename, MODE_READ)))
4104       return;
4105
4106     // default: for empty levels, use level template for custom elements
4107     level->use_custom_template = TRUE;
4108
4109     level->no_valid_file = FALSE;
4110   }
4111
4112   getFileChunkBE(file, chunk_name, NULL);
4113   if (strEqual(chunk_name, "RND1"))
4114   {
4115     getFile32BitBE(file);               // not used
4116
4117     getFileChunkBE(file, chunk_name, NULL);
4118     if (!strEqual(chunk_name, "CAVE"))
4119     {
4120       level->no_valid_file = TRUE;
4121
4122       Warn("unknown format of level file '%s'", filename);
4123
4124       closeFile(file);
4125
4126       return;
4127     }
4128   }
4129   else  // check for pre-2.0 file format with cookie string
4130   {
4131     strcpy(cookie, chunk_name);
4132     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4133       cookie[4] = '\0';
4134     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4135       cookie[strlen(cookie) - 1] = '\0';
4136
4137     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4138     {
4139       level->no_valid_file = TRUE;
4140
4141       Warn("unknown format of level file '%s'", filename);
4142
4143       closeFile(file);
4144
4145       return;
4146     }
4147
4148     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4149     {
4150       level->no_valid_file = TRUE;
4151
4152       Warn("unsupported version of level file '%s'", filename);
4153
4154       closeFile(file);
4155
4156       return;
4157     }
4158
4159     // pre-2.0 level files have no game version, so use file version here
4160     level->game_version = level->file_version;
4161   }
4162
4163   if (level->file_version < FILE_VERSION_1_2)
4164   {
4165     // level files from versions before 1.2.0 without chunk structure
4166     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4167     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4168   }
4169   else
4170   {
4171     static struct
4172     {
4173       char *name;
4174       int size;
4175       int (*loader)(File *, int, struct LevelInfo *);
4176     }
4177     chunk_info[] =
4178     {
4179       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4180       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4181       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4182       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4183       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4184       { "INFO", -1,                     LoadLevel_INFO },
4185       { "BODY", -1,                     LoadLevel_BODY },
4186       { "CONT", -1,                     LoadLevel_CONT },
4187       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4188       { "CNT3", -1,                     LoadLevel_CNT3 },
4189       { "CUS1", -1,                     LoadLevel_CUS1 },
4190       { "CUS2", -1,                     LoadLevel_CUS2 },
4191       { "CUS3", -1,                     LoadLevel_CUS3 },
4192       { "CUS4", -1,                     LoadLevel_CUS4 },
4193       { "GRP1", -1,                     LoadLevel_GRP1 },
4194       { "CONF", -1,                     LoadLevel_CONF },
4195       { "ELEM", -1,                     LoadLevel_ELEM },
4196       { "NOTE", -1,                     LoadLevel_NOTE },
4197       { "CUSX", -1,                     LoadLevel_CUSX },
4198       { "GRPX", -1,                     LoadLevel_GRPX },
4199       { "EMPX", -1,                     LoadLevel_EMPX },
4200
4201       {  NULL,  0,                      NULL }
4202     };
4203
4204     while (getFileChunkBE(file, chunk_name, &chunk_size))
4205     {
4206       int i = 0;
4207
4208       while (chunk_info[i].name != NULL &&
4209              !strEqual(chunk_name, chunk_info[i].name))
4210         i++;
4211
4212       if (chunk_info[i].name == NULL)
4213       {
4214         Warn("unknown chunk '%s' in level file '%s'",
4215              chunk_name, filename);
4216
4217         ReadUnusedBytesFromFile(file, chunk_size);
4218       }
4219       else if (chunk_info[i].size != -1 &&
4220                chunk_info[i].size != chunk_size)
4221       {
4222         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4223              chunk_size, chunk_name, filename);
4224
4225         ReadUnusedBytesFromFile(file, chunk_size);
4226       }
4227       else
4228       {
4229         // call function to load this level chunk
4230         int chunk_size_expected =
4231           (chunk_info[i].loader)(file, chunk_size, level);
4232
4233         if (chunk_size_expected < 0)
4234         {
4235           Warn("error reading chunk '%s' in level file '%s'",
4236                chunk_name, filename);
4237
4238           break;
4239         }
4240
4241         // the size of some chunks cannot be checked before reading other
4242         // chunks first (like "HEAD" and "BODY") that contain some header
4243         // information, so check them here
4244         if (chunk_size_expected != chunk_size)
4245         {
4246           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4247                chunk_size, chunk_name, filename);
4248
4249           break;
4250         }
4251       }
4252     }
4253   }
4254
4255   closeFile(file);
4256 }
4257
4258
4259 // ----------------------------------------------------------------------------
4260 // functions for loading BD level
4261 // ----------------------------------------------------------------------------
4262
4263 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4264 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4265
4266 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4267 {
4268   struct LevelInfo_BD *level_bd = level->native_bd_level;
4269   GdCave *cave = NULL;  // will be changed below
4270   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4271   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4272   int x, y;
4273
4274   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4275
4276   // cave and map newly allocated when set to defaults above
4277   cave = level_bd->cave;
4278
4279   // level type
4280   cave->intermission                    = level->bd_intermission;
4281
4282   // level settings
4283   cave->level_time[0]                   = level->time;
4284   cave->level_diamonds[0]               = level->gems_needed;
4285
4286   // game timing
4287   cave->scheduling                      = level->bd_scheduling_type;
4288   cave->pal_timing                      = level->bd_pal_timing;
4289   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4290   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4291   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4292   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4293
4294   // scores
4295   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4296   cave->diamond_value                   = level->score[SC_EMERALD];
4297   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4298
4299   // compatibility settings
4300   cave->lineshift                       = level->bd_line_shifting_borders;
4301   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4302   cave->short_explosions                = level->bd_short_explosions;
4303
4304   // player properties
4305   cave->diagonal_movements              = level->bd_diagonal_movements;
4306   cave->active_is_first_found           = level->bd_topmost_player_active;
4307   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4308   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4309   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4310   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4311
4312   // element properties
4313   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4314   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4315   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4316   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4317   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4318   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4319   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4320   cave->magic_timer_zero_is_infinite    = level->bd_magic_wall_zero_infinite;
4321   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4322   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4323   cave->magic_wall_breakscan            = level->bd_magic_wall_break_scan;
4324
4325   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4326   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4327   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4328   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4329   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4330   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4331   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4332
4333   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4334   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4335   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4336   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4337   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4338   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4339   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4340   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4341   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4342   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4343   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4344
4345   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4346   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4347   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4348   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4349   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4350   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4351
4352   cave->slime_predictable               = level->bd_slime_is_predictable;
4353   cave->slime_correct_random            = level->bd_slime_correct_random;
4354   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4355   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4356   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4357   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4358   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4359   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4360   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4361   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4362   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4363   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4364
4365   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4366   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4367   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4368
4369   cave->biter_delay_frame               = level->bd_biter_move_delay;
4370   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4371
4372   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4373
4374   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4375
4376   cave->replicators_active              = level->bd_replicators_active;
4377   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4378
4379   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4380   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4381
4382   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4383
4384   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4385
4386   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4387   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4388   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4389
4390   cave->infinite_rockets                = level->bd_infinite_rockets;
4391
4392   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4393   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4394
4395   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4396   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4397
4398   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4399   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4400   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4401
4402   cave->gravity                         = level->bd_gravity_direction;
4403   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4404   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4405   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4406
4407   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4408   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4409   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4410   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4411
4412   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4413   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4414   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4415   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4416   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4417   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4418
4419   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4420   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4421   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4422   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4423
4424   cave->colorb                          = level->bd_color_b;
4425   cave->color0                          = level->bd_color_0;
4426   cave->color1                          = level->bd_color_1;
4427   cave->color2                          = level->bd_color_2;
4428   cave->color3                          = level->bd_color_3;
4429   cave->color4                          = level->bd_color_4;
4430   cave->color5                          = level->bd_color_5;
4431
4432   // level name
4433   strncpy(cave->name, level->name, sizeof(GdString));
4434   cave->name[sizeof(GdString) - 1] = '\0';
4435
4436   // playfield elements
4437   for (x = 0; x < cave->w; x++)
4438     for (y = 0; y < cave->h; y++)
4439       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4440 }
4441
4442 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4443 {
4444   struct LevelInfo_BD *level_bd = level->native_bd_level;
4445   GdCave *cave = level_bd->cave;
4446   int bd_level_nr = level_bd->level_nr;
4447   int x, y;
4448
4449   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4450   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4451
4452   // level type
4453   level->bd_intermission                = cave->intermission;
4454
4455   // level settings
4456   level->time                           = cave->level_time[bd_level_nr];
4457   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4458
4459   // game timing
4460   level->bd_scheduling_type             = cave->scheduling;
4461   level->bd_pal_timing                  = cave->pal_timing;
4462   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4463   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4464   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4465   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4466
4467   // scores
4468   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4469   level->score[SC_EMERALD]              = cave->diamond_value;
4470   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4471
4472   // compatibility settings
4473   level->bd_line_shifting_borders       = cave->lineshift;
4474   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4475   level->bd_short_explosions            = cave->short_explosions;
4476
4477   // player properties
4478   level->bd_diagonal_movements          = cave->diagonal_movements;
4479   level->bd_topmost_player_active       = cave->active_is_first_found;
4480   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4481   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4482   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4483   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4484
4485   // element properties
4486   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4487   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4488   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4489   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4490   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4491   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4492   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4493   level->bd_magic_wall_zero_infinite    = cave->magic_timer_zero_is_infinite;
4494   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4495   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4496   level->bd_magic_wall_break_scan       = cave->magic_wall_breakscan;
4497
4498   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4499   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4500   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4501   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4502   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4503   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4504   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4505
4506   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4507   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4508   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4509   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4510   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4511   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4512   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4513   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4514   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4515   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4516   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4517
4518   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4519   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4520   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4521   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4522   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4523   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4524
4525   level->bd_slime_is_predictable        = cave->slime_predictable;
4526   level->bd_slime_correct_random        = cave->slime_correct_random;
4527   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4528   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4529   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4530   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4531   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4532   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4533   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4534   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4535   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4536   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4537
4538   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4539   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4540   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4541
4542   level->bd_biter_move_delay            = cave->biter_delay_frame;
4543   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4544
4545   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4546
4547   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4548
4549   level->bd_replicators_active          = cave->replicators_active;
4550   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4551
4552   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4553   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4554
4555   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4556
4557   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4558
4559   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4560   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4561   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4562
4563   level->bd_infinite_rockets            = cave->infinite_rockets;
4564
4565   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4566   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4567
4568   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4569   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4570
4571   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4572   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4573   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4574
4575   level->bd_gravity_direction           = cave->gravity;
4576   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4577   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4578   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4579
4580   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4581   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4582   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4583   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4584
4585   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4586   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4587   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4588   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4589   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4590   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4591
4592   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4593   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4594   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4595   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4596
4597   level->bd_color_b                     = cave->colorb;
4598   level->bd_color_0                     = cave->color0;
4599   level->bd_color_1                     = cave->color1;
4600   level->bd_color_2                     = cave->color2;
4601   level->bd_color_3                     = cave->color3;
4602   level->bd_color_4                     = cave->color4;
4603   level->bd_color_5                     = cave->color5;
4604
4605   // set default color type and colors for BD style level colors
4606   SetDefaultLevelColorType_BD();
4607   SetDefaultLevelColors_BD();
4608
4609   // level name
4610   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4611
4612   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4613   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4614
4615   // playfield elements
4616   for (x = 0; x < level->fieldx; x++)
4617     for (y = 0; y < level->fieldy; y++)
4618       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4619
4620   checked_free(cave_name);
4621 }
4622
4623 static void setTapeInfoToDefaults(void);
4624
4625 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4626 {
4627   struct LevelInfo_BD *level_bd = level->native_bd_level;
4628   GdCave *cave = level_bd->cave;
4629   GdReplay *replay = level_bd->replay;
4630   int i;
4631
4632   if (replay == NULL)
4633     return;
4634
4635   // always start with reliable default values
4636   setTapeInfoToDefaults();
4637
4638   tape.level_nr = level_nr;             // (currently not used)
4639   tape.random_seed = replay->seed;
4640
4641   TapeSetDateFromIsoDateString(replay->date);
4642
4643   tape.counter = 0;
4644   tape.pos[tape.counter].delay = 0;
4645
4646   tape.bd_replay = TRUE;
4647
4648   // all time calculations only used to display approximate tape time
4649   int cave_speed = cave->speed;
4650   int milliseconds_game = 0;
4651   int milliseconds_elapsed = 20;
4652
4653   for (i = 0; i < replay->movements->len; i++)
4654   {
4655     int replay_action = replay->movements->data[i];
4656     int tape_action = map_action_BD_to_RND(replay_action);
4657     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4658     boolean success = 0;
4659
4660     while (1)
4661     {
4662       success = TapeAddAction(action);
4663
4664       milliseconds_game += milliseconds_elapsed;
4665
4666       if (milliseconds_game >= cave_speed)
4667       {
4668         milliseconds_game -= cave_speed;
4669
4670         break;
4671       }
4672     }
4673
4674     tape.counter++;
4675     tape.pos[tape.counter].delay = 0;
4676     tape.pos[tape.counter].action[0] = 0;
4677
4678     if (!success)
4679     {
4680       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4681
4682       break;
4683     }
4684   }
4685
4686   TapeHaltRecording();
4687
4688   if (!replay->success)
4689     Warn("BD replay is marked as not successful");
4690 }
4691
4692
4693 // ----------------------------------------------------------------------------
4694 // functions for loading EM level
4695 // ----------------------------------------------------------------------------
4696
4697 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4698 {
4699   static int ball_xy[8][2] =
4700   {
4701     { 0, 0 },
4702     { 1, 0 },
4703     { 2, 0 },
4704     { 0, 1 },
4705     { 2, 1 },
4706     { 0, 2 },
4707     { 1, 2 },
4708     { 2, 2 },
4709   };
4710   struct LevelInfo_EM *level_em = level->native_em_level;
4711   struct CAVE *cav = level_em->cav;
4712   int i, j, x, y;
4713
4714   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4715   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4716
4717   cav->time_seconds     = level->time;
4718   cav->gems_needed      = level->gems_needed;
4719
4720   cav->emerald_score    = level->score[SC_EMERALD];
4721   cav->diamond_score    = level->score[SC_DIAMOND];
4722   cav->alien_score      = level->score[SC_ROBOT];
4723   cav->tank_score       = level->score[SC_SPACESHIP];
4724   cav->bug_score        = level->score[SC_BUG];
4725   cav->eater_score      = level->score[SC_YAMYAM];
4726   cav->nut_score        = level->score[SC_NUT];
4727   cav->dynamite_score   = level->score[SC_DYNAMITE];
4728   cav->key_score        = level->score[SC_KEY];
4729   cav->exit_score       = level->score[SC_TIME_BONUS];
4730
4731   cav->num_eater_arrays = level->num_yamyam_contents;
4732
4733   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4734     for (y = 0; y < 3; y++)
4735       for (x = 0; x < 3; x++)
4736         cav->eater_array[i][y * 3 + x] =
4737           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4738
4739   cav->amoeba_time              = level->amoeba_speed;
4740   cav->wonderwall_time          = level->time_magic_wall;
4741   cav->wheel_time               = level->time_wheel;
4742
4743   cav->android_move_time        = level->android_move_time;
4744   cav->android_clone_time       = level->android_clone_time;
4745   cav->ball_random              = level->ball_random;
4746   cav->ball_active              = level->ball_active_initial;
4747   cav->ball_time                = level->ball_time;
4748   cav->num_ball_arrays          = level->num_ball_contents;
4749
4750   cav->lenses_score             = level->lenses_score;
4751   cav->magnify_score            = level->magnify_score;
4752   cav->slurp_score              = level->slurp_score;
4753
4754   cav->lenses_time              = level->lenses_time;
4755   cav->magnify_time             = level->magnify_time;
4756
4757   cav->wind_time = 9999;
4758   cav->wind_direction =
4759     map_direction_RND_to_EM(level->wind_direction_initial);
4760
4761   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4762     for (j = 0; j < 8; j++)
4763       cav->ball_array[i][j] =
4764         map_element_RND_to_EM_cave(level->ball_content[i].
4765                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4766
4767   map_android_clone_elements_RND_to_EM(level);
4768
4769   // first fill the complete playfield with the empty space element
4770   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4771     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4772       cav->cave[x][y] = Cblank;
4773
4774   // then copy the real level contents from level file into the playfield
4775   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4776   {
4777     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4778
4779     if (level->field[x][y] == EL_AMOEBA_DEAD)
4780       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4781
4782     cav->cave[x][y] = new_element;
4783   }
4784
4785   for (i = 0; i < MAX_PLAYERS; i++)
4786   {
4787     cav->player_x[i] = -1;
4788     cav->player_y[i] = -1;
4789   }
4790
4791   // initialize player positions and delete players from the playfield
4792   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4793   {
4794     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4795     {
4796       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4797
4798       cav->player_x[player_nr] = x;
4799       cav->player_y[player_nr] = y;
4800
4801       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4802     }
4803   }
4804 }
4805
4806 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4807 {
4808   static int ball_xy[8][2] =
4809   {
4810     { 0, 0 },
4811     { 1, 0 },
4812     { 2, 0 },
4813     { 0, 1 },
4814     { 2, 1 },
4815     { 0, 2 },
4816     { 1, 2 },
4817     { 2, 2 },
4818   };
4819   struct LevelInfo_EM *level_em = level->native_em_level;
4820   struct CAVE *cav = level_em->cav;
4821   int i, j, x, y;
4822
4823   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4824   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4825
4826   level->time        = cav->time_seconds;
4827   level->gems_needed = cav->gems_needed;
4828
4829   sprintf(level->name, "Level %d", level->file_info.nr);
4830
4831   level->score[SC_EMERALD]      = cav->emerald_score;
4832   level->score[SC_DIAMOND]      = cav->diamond_score;
4833   level->score[SC_ROBOT]        = cav->alien_score;
4834   level->score[SC_SPACESHIP]    = cav->tank_score;
4835   level->score[SC_BUG]          = cav->bug_score;
4836   level->score[SC_YAMYAM]       = cav->eater_score;
4837   level->score[SC_NUT]          = cav->nut_score;
4838   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4839   level->score[SC_KEY]          = cav->key_score;
4840   level->score[SC_TIME_BONUS]   = cav->exit_score;
4841
4842   level->num_yamyam_contents    = cav->num_eater_arrays;
4843
4844   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4845     for (y = 0; y < 3; y++)
4846       for (x = 0; x < 3; x++)
4847         level->yamyam_content[i].e[x][y] =
4848           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4849
4850   level->amoeba_speed           = cav->amoeba_time;
4851   level->time_magic_wall        = cav->wonderwall_time;
4852   level->time_wheel             = cav->wheel_time;
4853
4854   level->android_move_time      = cav->android_move_time;
4855   level->android_clone_time     = cav->android_clone_time;
4856   level->ball_random            = cav->ball_random;
4857   level->ball_active_initial    = cav->ball_active;
4858   level->ball_time              = cav->ball_time;
4859   level->num_ball_contents      = cav->num_ball_arrays;
4860
4861   level->lenses_score           = cav->lenses_score;
4862   level->magnify_score          = cav->magnify_score;
4863   level->slurp_score            = cav->slurp_score;
4864
4865   level->lenses_time            = cav->lenses_time;
4866   level->magnify_time           = cav->magnify_time;
4867
4868   level->wind_direction_initial =
4869     map_direction_EM_to_RND(cav->wind_direction);
4870
4871   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4872     for (j = 0; j < 8; j++)
4873       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4874         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4875
4876   map_android_clone_elements_EM_to_RND(level);
4877
4878   // convert the playfield (some elements need special treatment)
4879   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4880   {
4881     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4882
4883     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4884       new_element = EL_AMOEBA_DEAD;
4885
4886     level->field[x][y] = new_element;
4887   }
4888
4889   for (i = 0; i < MAX_PLAYERS; i++)
4890   {
4891     // in case of all players set to the same field, use the first player
4892     int nr = MAX_PLAYERS - i - 1;
4893     int jx = cav->player_x[nr];
4894     int jy = cav->player_y[nr];
4895
4896     if (jx != -1 && jy != -1)
4897       level->field[jx][jy] = EL_PLAYER_1 + nr;
4898   }
4899
4900   // time score is counted for each 10 seconds left in Emerald Mine levels
4901   level->time_score_base = 10;
4902 }
4903
4904
4905 // ----------------------------------------------------------------------------
4906 // functions for loading SP level
4907 // ----------------------------------------------------------------------------
4908
4909 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4910 {
4911   struct LevelInfo_SP *level_sp = level->native_sp_level;
4912   LevelInfoType *header = &level_sp->header;
4913   int i, x, y;
4914
4915   level_sp->width  = level->fieldx;
4916   level_sp->height = level->fieldy;
4917
4918   for (x = 0; x < level->fieldx; x++)
4919     for (y = 0; y < level->fieldy; y++)
4920       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4921
4922   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4923
4924   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4925     header->LevelTitle[i] = level->name[i];
4926   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4927
4928   header->InfotronsNeeded = level->gems_needed;
4929
4930   header->SpecialPortCount = 0;
4931
4932   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4933   {
4934     boolean gravity_port_found = FALSE;
4935     boolean gravity_port_valid = FALSE;
4936     int gravity_port_flag;
4937     int gravity_port_base_element;
4938     int element = level->field[x][y];
4939
4940     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4941         element <= EL_SP_GRAVITY_ON_PORT_UP)
4942     {
4943       gravity_port_found = TRUE;
4944       gravity_port_valid = TRUE;
4945       gravity_port_flag = 1;
4946       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4947     }
4948     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4949              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4950     {
4951       gravity_port_found = TRUE;
4952       gravity_port_valid = TRUE;
4953       gravity_port_flag = 0;
4954       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4955     }
4956     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4957              element <= EL_SP_GRAVITY_PORT_UP)
4958     {
4959       // change R'n'D style gravity inverting special port to normal port
4960       // (there are no gravity inverting ports in native Supaplex engine)
4961
4962       gravity_port_found = TRUE;
4963       gravity_port_valid = FALSE;
4964       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4965     }
4966
4967     if (gravity_port_found)
4968     {
4969       if (gravity_port_valid &&
4970           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4971       {
4972         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4973
4974         port->PortLocation = (y * level->fieldx + x) * 2;
4975         port->Gravity = gravity_port_flag;
4976
4977         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4978
4979         header->SpecialPortCount++;
4980       }
4981       else
4982       {
4983         // change special gravity port to normal port
4984
4985         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4986       }
4987
4988       level_sp->playfield[x][y] = element - EL_SP_START;
4989     }
4990   }
4991 }
4992
4993 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4994 {
4995   struct LevelInfo_SP *level_sp = level->native_sp_level;
4996   LevelInfoType *header = &level_sp->header;
4997   boolean num_invalid_elements = 0;
4998   int i, j, x, y;
4999
5000   level->fieldx = level_sp->width;
5001   level->fieldy = level_sp->height;
5002
5003   for (x = 0; x < level->fieldx; x++)
5004   {
5005     for (y = 0; y < level->fieldy; y++)
5006     {
5007       int element_old = level_sp->playfield[x][y];
5008       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
5009
5010       if (element_new == EL_UNKNOWN)
5011       {
5012         num_invalid_elements++;
5013
5014         Debug("level:native:SP", "invalid element %d at position %d, %d",
5015               element_old, x, y);
5016       }
5017
5018       level->field[x][y] = element_new;
5019     }
5020   }
5021
5022   if (num_invalid_elements > 0)
5023     Warn("found %d invalid elements%s", num_invalid_elements,
5024          (!options.debug ? " (use '--debug' for more details)" : ""));
5025
5026   for (i = 0; i < MAX_PLAYERS; i++)
5027     level->initial_player_gravity[i] =
5028       (header->InitialGravity == 1 ? TRUE : FALSE);
5029
5030   // skip leading spaces
5031   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
5032     if (header->LevelTitle[i] != ' ')
5033       break;
5034
5035   // copy level title
5036   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
5037     level->name[j] = header->LevelTitle[i];
5038   level->name[j] = '\0';
5039
5040   // cut trailing spaces
5041   for (; j > 0; j--)
5042     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
5043       level->name[j - 1] = '\0';
5044
5045   level->gems_needed = header->InfotronsNeeded;
5046
5047   for (i = 0; i < header->SpecialPortCount; i++)
5048   {
5049     SpecialPortType *port = &header->SpecialPort[i];
5050     int port_location = port->PortLocation;
5051     int gravity = port->Gravity;
5052     int port_x, port_y, port_element;
5053
5054     port_x = (port_location / 2) % level->fieldx;
5055     port_y = (port_location / 2) / level->fieldx;
5056
5057     if (port_x < 0 || port_x >= level->fieldx ||
5058         port_y < 0 || port_y >= level->fieldy)
5059     {
5060       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
5061
5062       continue;
5063     }
5064
5065     port_element = level->field[port_x][port_y];
5066
5067     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
5068         port_element > EL_SP_GRAVITY_PORT_UP)
5069     {
5070       Warn("no special port at position (%d, %d)", port_x, port_y);
5071
5072       continue;
5073     }
5074
5075     // change previous (wrong) gravity inverting special port to either
5076     // gravity enabling special port or gravity disabling special port
5077     level->field[port_x][port_y] +=
5078       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5079        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5080   }
5081
5082   // change special gravity ports without database entries to normal ports
5083   for (x = 0; x < level->fieldx; x++)
5084     for (y = 0; y < level->fieldy; y++)
5085       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5086           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5087         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5088
5089   level->time = 0;                      // no time limit
5090   level->amoeba_speed = 0;
5091   level->time_magic_wall = 0;
5092   level->time_wheel = 0;
5093   level->amoeba_content = EL_EMPTY;
5094
5095   // original Supaplex does not use score values -- rate by playing time
5096   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5097     level->score[i] = 0;
5098
5099   level->rate_time_over_score = TRUE;
5100
5101   // there are no yamyams in supaplex levels
5102   for (i = 0; i < level->num_yamyam_contents; i++)
5103     for (x = 0; x < 3; x++)
5104       for (y = 0; y < 3; y++)
5105         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5106 }
5107
5108 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5109 {
5110   struct LevelInfo_SP *level_sp = level->native_sp_level;
5111   struct DemoInfo_SP *demo = &level_sp->demo;
5112   int i, j;
5113
5114   // always start with reliable default values
5115   demo->is_available = FALSE;
5116   demo->length = 0;
5117
5118   if (TAPE_IS_EMPTY(tape))
5119     return;
5120
5121   demo->level_nr = tape.level_nr;       // (currently not used)
5122
5123   level_sp->header.DemoRandomSeed = tape.random_seed;
5124
5125   demo->length = 0;
5126
5127   for (i = 0; i < tape.length; i++)
5128   {
5129     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5130     int demo_repeat = tape.pos[i].delay;
5131     int demo_entries = (demo_repeat + 15) / 16;
5132
5133     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5134     {
5135       Warn("tape truncated: size exceeds maximum SP demo size %d",
5136            SP_MAX_TAPE_LEN);
5137
5138       break;
5139     }
5140
5141     for (j = 0; j < demo_repeat / 16; j++)
5142       demo->data[demo->length++] = 0xf0 | demo_action;
5143
5144     if (demo_repeat % 16)
5145       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5146   }
5147
5148   demo->is_available = TRUE;
5149 }
5150
5151 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5152 {
5153   struct LevelInfo_SP *level_sp = level->native_sp_level;
5154   struct DemoInfo_SP *demo = &level_sp->demo;
5155   char *filename = level->file_info.filename;
5156   int i;
5157
5158   // always start with reliable default values
5159   setTapeInfoToDefaults();
5160
5161   if (!demo->is_available)
5162     return;
5163
5164   tape.level_nr = demo->level_nr;       // (currently not used)
5165   tape.random_seed = level_sp->header.DemoRandomSeed;
5166
5167   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5168
5169   tape.counter = 0;
5170   tape.pos[tape.counter].delay = 0;
5171
5172   for (i = 0; i < demo->length; i++)
5173   {
5174     int demo_action = demo->data[i] & 0x0f;
5175     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5176     int tape_action = map_key_SP_to_RND(demo_action);
5177     int tape_repeat = demo_repeat + 1;
5178     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5179     boolean success = 0;
5180     int j;
5181
5182     for (j = 0; j < tape_repeat; j++)
5183       success = TapeAddAction(action);
5184
5185     if (!success)
5186     {
5187       Warn("SP demo truncated: size exceeds maximum tape size %d",
5188            MAX_TAPE_LEN);
5189
5190       break;
5191     }
5192   }
5193
5194   TapeHaltRecording();
5195 }
5196
5197
5198 // ----------------------------------------------------------------------------
5199 // functions for loading MM level
5200 // ----------------------------------------------------------------------------
5201
5202 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5203 {
5204   struct LevelInfo_MM *level_mm = level->native_mm_level;
5205   int i, x, y;
5206
5207   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5208   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5209
5210   level_mm->time = level->time;
5211   level_mm->kettles_needed = level->gems_needed;
5212   level_mm->auto_count_kettles = level->auto_count_gems;
5213
5214   level_mm->mm_laser_red   = level->mm_laser_red;
5215   level_mm->mm_laser_green = level->mm_laser_green;
5216   level_mm->mm_laser_blue  = level->mm_laser_blue;
5217
5218   level_mm->df_laser_red   = level->df_laser_red;
5219   level_mm->df_laser_green = level->df_laser_green;
5220   level_mm->df_laser_blue  = level->df_laser_blue;
5221
5222   strcpy(level_mm->name, level->name);
5223   strcpy(level_mm->author, level->author);
5224
5225   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5226   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5227   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5228   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5229   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5230
5231   level_mm->amoeba_speed = level->amoeba_speed;
5232   level_mm->time_fuse    = level->mm_time_fuse;
5233   level_mm->time_bomb    = level->mm_time_bomb;
5234   level_mm->time_ball    = level->mm_time_ball;
5235   level_mm->time_block   = level->mm_time_block;
5236
5237   level_mm->num_ball_contents = level->num_mm_ball_contents;
5238   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5239   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5240   level_mm->explode_ball = level->explode_mm_ball;
5241
5242   for (i = 0; i < level->num_mm_ball_contents; i++)
5243     level_mm->ball_content[i] =
5244       map_element_RND_to_MM(level->mm_ball_content[i]);
5245
5246   for (x = 0; x < level->fieldx; x++)
5247     for (y = 0; y < level->fieldy; y++)
5248       Ur[x][y] =
5249         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5250 }
5251
5252 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5253 {
5254   struct LevelInfo_MM *level_mm = level->native_mm_level;
5255   int i, x, y;
5256
5257   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5258   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5259
5260   level->time = level_mm->time;
5261   level->gems_needed = level_mm->kettles_needed;
5262   level->auto_count_gems = level_mm->auto_count_kettles;
5263
5264   level->mm_laser_red   = level_mm->mm_laser_red;
5265   level->mm_laser_green = level_mm->mm_laser_green;
5266   level->mm_laser_blue  = level_mm->mm_laser_blue;
5267
5268   level->df_laser_red   = level_mm->df_laser_red;
5269   level->df_laser_green = level_mm->df_laser_green;
5270   level->df_laser_blue  = level_mm->df_laser_blue;
5271
5272   strcpy(level->name, level_mm->name);
5273
5274   // only overwrite author from 'levelinfo.conf' if author defined in level
5275   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5276     strcpy(level->author, level_mm->author);
5277
5278   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5279   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5280   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5281   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5282   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5283
5284   level->amoeba_speed  = level_mm->amoeba_speed;
5285   level->mm_time_fuse  = level_mm->time_fuse;
5286   level->mm_time_bomb  = level_mm->time_bomb;
5287   level->mm_time_ball  = level_mm->time_ball;
5288   level->mm_time_block = level_mm->time_block;
5289
5290   level->num_mm_ball_contents = level_mm->num_ball_contents;
5291   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5292   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5293   level->explode_mm_ball = level_mm->explode_ball;
5294
5295   for (i = 0; i < level->num_mm_ball_contents; i++)
5296     level->mm_ball_content[i] =
5297       map_element_MM_to_RND(level_mm->ball_content[i]);
5298
5299   for (x = 0; x < level->fieldx; x++)
5300     for (y = 0; y < level->fieldy; y++)
5301       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5302 }
5303
5304
5305 // ----------------------------------------------------------------------------
5306 // functions for loading DC level
5307 // ----------------------------------------------------------------------------
5308
5309 #define DC_LEVEL_HEADER_SIZE            344
5310
5311 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5312                                         boolean init)
5313 {
5314   static int last_data_encoded;
5315   static int offset1;
5316   static int offset2;
5317   int diff;
5318   int diff_hi, diff_lo;
5319   int data_hi, data_lo;
5320   unsigned short data_decoded;
5321
5322   if (init)
5323   {
5324     last_data_encoded = 0;
5325     offset1 = -1;
5326     offset2 = 0;
5327
5328     return 0;
5329   }
5330
5331   diff = data_encoded - last_data_encoded;
5332   diff_hi = diff & ~0xff;
5333   diff_lo = diff &  0xff;
5334
5335   offset2 += diff_lo;
5336
5337   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5338   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5339   data_hi = data_hi & 0xff00;
5340
5341   data_decoded = data_hi | data_lo;
5342
5343   last_data_encoded = data_encoded;
5344
5345   offset1 = (offset1 + 1) % 31;
5346   offset2 = offset2 & 0xff;
5347
5348   return data_decoded;
5349 }
5350
5351 static int getMappedElement_DC(int element)
5352 {
5353   switch (element)
5354   {
5355     case 0x0000:
5356       element = EL_ROCK;
5357       break;
5358
5359       // 0x0117 - 0x036e: (?)
5360       // EL_DIAMOND
5361
5362       // 0x042d - 0x0684: (?)
5363       // EL_EMERALD
5364
5365     case 0x06f1:
5366       element = EL_NUT;
5367       break;
5368
5369     case 0x074c:
5370       element = EL_BOMB;
5371       break;
5372
5373     case 0x07a4:
5374       element = EL_PEARL;
5375       break;
5376
5377     case 0x0823:
5378       element = EL_CRYSTAL;
5379       break;
5380
5381     case 0x0e77:        // quicksand (boulder)
5382       element = EL_QUICKSAND_FAST_FULL;
5383       break;
5384
5385     case 0x0e99:        // slow quicksand (boulder)
5386       element = EL_QUICKSAND_FULL;
5387       break;
5388
5389     case 0x0ed2:
5390       element = EL_EM_EXIT_OPEN;
5391       break;
5392
5393     case 0x0ee3:
5394       element = EL_EM_EXIT_CLOSED;
5395       break;
5396
5397     case 0x0eeb:
5398       element = EL_EM_STEEL_EXIT_OPEN;
5399       break;
5400
5401     case 0x0efc:
5402       element = EL_EM_STEEL_EXIT_CLOSED;
5403       break;
5404
5405     case 0x0f4f:        // dynamite (lit 1)
5406       element = EL_EM_DYNAMITE_ACTIVE;
5407       break;
5408
5409     case 0x0f57:        // dynamite (lit 2)
5410       element = EL_EM_DYNAMITE_ACTIVE;
5411       break;
5412
5413     case 0x0f5f:        // dynamite (lit 3)
5414       element = EL_EM_DYNAMITE_ACTIVE;
5415       break;
5416
5417     case 0x0f67:        // dynamite (lit 4)
5418       element = EL_EM_DYNAMITE_ACTIVE;
5419       break;
5420
5421     case 0x0f81:
5422     case 0x0f82:
5423     case 0x0f83:
5424     case 0x0f84:
5425       element = EL_AMOEBA_WET;
5426       break;
5427
5428     case 0x0f85:
5429       element = EL_AMOEBA_DROP;
5430       break;
5431
5432     case 0x0fb9:
5433       element = EL_DC_MAGIC_WALL;
5434       break;
5435
5436     case 0x0fd0:
5437       element = EL_SPACESHIP_UP;
5438       break;
5439
5440     case 0x0fd9:
5441       element = EL_SPACESHIP_DOWN;
5442       break;
5443
5444     case 0x0ff1:
5445       element = EL_SPACESHIP_LEFT;
5446       break;
5447
5448     case 0x0ff9:
5449       element = EL_SPACESHIP_RIGHT;
5450       break;
5451
5452     case 0x1057:
5453       element = EL_BUG_UP;
5454       break;
5455
5456     case 0x1060:
5457       element = EL_BUG_DOWN;
5458       break;
5459
5460     case 0x1078:
5461       element = EL_BUG_LEFT;
5462       break;
5463
5464     case 0x1080:
5465       element = EL_BUG_RIGHT;
5466       break;
5467
5468     case 0x10de:
5469       element = EL_MOLE_UP;
5470       break;
5471
5472     case 0x10e7:
5473       element = EL_MOLE_DOWN;
5474       break;
5475
5476     case 0x10ff:
5477       element = EL_MOLE_LEFT;
5478       break;
5479
5480     case 0x1107:
5481       element = EL_MOLE_RIGHT;
5482       break;
5483
5484     case 0x11c0:
5485       element = EL_ROBOT;
5486       break;
5487
5488     case 0x13f5:
5489       element = EL_YAMYAM_UP;
5490       break;
5491
5492     case 0x1425:
5493       element = EL_SWITCHGATE_OPEN;
5494       break;
5495
5496     case 0x1426:
5497       element = EL_SWITCHGATE_CLOSED;
5498       break;
5499
5500     case 0x1437:
5501       element = EL_DC_SWITCHGATE_SWITCH_UP;
5502       break;
5503
5504     case 0x143a:
5505       element = EL_TIMEGATE_CLOSED;
5506       break;
5507
5508     case 0x144c:        // conveyor belt switch (green)
5509       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5510       break;
5511
5512     case 0x144f:        // conveyor belt switch (red)
5513       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5514       break;
5515
5516     case 0x1452:        // conveyor belt switch (blue)
5517       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5518       break;
5519
5520     case 0x145b:
5521       element = EL_CONVEYOR_BELT_3_MIDDLE;
5522       break;
5523
5524     case 0x1463:
5525       element = EL_CONVEYOR_BELT_3_LEFT;
5526       break;
5527
5528     case 0x146b:
5529       element = EL_CONVEYOR_BELT_3_RIGHT;
5530       break;
5531
5532     case 0x1473:
5533       element = EL_CONVEYOR_BELT_1_MIDDLE;
5534       break;
5535
5536     case 0x147b:
5537       element = EL_CONVEYOR_BELT_1_LEFT;
5538       break;
5539
5540     case 0x1483:
5541       element = EL_CONVEYOR_BELT_1_RIGHT;
5542       break;
5543
5544     case 0x148b:
5545       element = EL_CONVEYOR_BELT_4_MIDDLE;
5546       break;
5547
5548     case 0x1493:
5549       element = EL_CONVEYOR_BELT_4_LEFT;
5550       break;
5551
5552     case 0x149b:
5553       element = EL_CONVEYOR_BELT_4_RIGHT;
5554       break;
5555
5556     case 0x14ac:
5557       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5558       break;
5559
5560     case 0x14bd:
5561       element = EL_EXPANDABLE_WALL_VERTICAL;
5562       break;
5563
5564     case 0x14c6:
5565       element = EL_EXPANDABLE_WALL_ANY;
5566       break;
5567
5568     case 0x14ce:        // growing steel wall (left/right)
5569       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5570       break;
5571
5572     case 0x14df:        // growing steel wall (up/down)
5573       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5574       break;
5575
5576     case 0x14e8:        // growing steel wall (up/down/left/right)
5577       element = EL_EXPANDABLE_STEELWALL_ANY;
5578       break;
5579
5580     case 0x14e9:
5581       element = EL_SHIELD_DEADLY;
5582       break;
5583
5584     case 0x1501:
5585       element = EL_EXTRA_TIME;
5586       break;
5587
5588     case 0x154f:
5589       element = EL_ACID;
5590       break;
5591
5592     case 0x1577:
5593       element = EL_EMPTY_SPACE;
5594       break;
5595
5596     case 0x1578:        // quicksand (empty)
5597       element = EL_QUICKSAND_FAST_EMPTY;
5598       break;
5599
5600     case 0x1579:        // slow quicksand (empty)
5601       element = EL_QUICKSAND_EMPTY;
5602       break;
5603
5604       // 0x157c - 0x158b:
5605       // EL_SAND
5606
5607       // 0x1590 - 0x159f:
5608       // EL_DC_LANDMINE
5609
5610     case 0x15a0:
5611       element = EL_EM_DYNAMITE;
5612       break;
5613
5614     case 0x15a1:        // key (red)
5615       element = EL_EM_KEY_1;
5616       break;
5617
5618     case 0x15a2:        // key (yellow)
5619       element = EL_EM_KEY_2;
5620       break;
5621
5622     case 0x15a3:        // key (blue)
5623       element = EL_EM_KEY_4;
5624       break;
5625
5626     case 0x15a4:        // key (green)
5627       element = EL_EM_KEY_3;
5628       break;
5629
5630     case 0x15a5:        // key (white)
5631       element = EL_DC_KEY_WHITE;
5632       break;
5633
5634     case 0x15a6:
5635       element = EL_WALL_SLIPPERY;
5636       break;
5637
5638     case 0x15a7:
5639       element = EL_WALL;
5640       break;
5641
5642     case 0x15a8:        // wall (not round)
5643       element = EL_WALL;
5644       break;
5645
5646     case 0x15a9:        // (blue)
5647       element = EL_CHAR_A;
5648       break;
5649
5650     case 0x15aa:        // (blue)
5651       element = EL_CHAR_B;
5652       break;
5653
5654     case 0x15ab:        // (blue)
5655       element = EL_CHAR_C;
5656       break;
5657
5658     case 0x15ac:        // (blue)
5659       element = EL_CHAR_D;
5660       break;
5661
5662     case 0x15ad:        // (blue)
5663       element = EL_CHAR_E;
5664       break;
5665
5666     case 0x15ae:        // (blue)
5667       element = EL_CHAR_F;
5668       break;
5669
5670     case 0x15af:        // (blue)
5671       element = EL_CHAR_G;
5672       break;
5673
5674     case 0x15b0:        // (blue)
5675       element = EL_CHAR_H;
5676       break;
5677
5678     case 0x15b1:        // (blue)
5679       element = EL_CHAR_I;
5680       break;
5681
5682     case 0x15b2:        // (blue)
5683       element = EL_CHAR_J;
5684       break;
5685
5686     case 0x15b3:        // (blue)
5687       element = EL_CHAR_K;
5688       break;
5689
5690     case 0x15b4:        // (blue)
5691       element = EL_CHAR_L;
5692       break;
5693
5694     case 0x15b5:        // (blue)
5695       element = EL_CHAR_M;
5696       break;
5697
5698     case 0x15b6:        // (blue)
5699       element = EL_CHAR_N;
5700       break;
5701
5702     case 0x15b7:        // (blue)
5703       element = EL_CHAR_O;
5704       break;
5705
5706     case 0x15b8:        // (blue)
5707       element = EL_CHAR_P;
5708       break;
5709
5710     case 0x15b9:        // (blue)
5711       element = EL_CHAR_Q;
5712       break;
5713
5714     case 0x15ba:        // (blue)
5715       element = EL_CHAR_R;
5716       break;
5717
5718     case 0x15bb:        // (blue)
5719       element = EL_CHAR_S;
5720       break;
5721
5722     case 0x15bc:        // (blue)
5723       element = EL_CHAR_T;
5724       break;
5725
5726     case 0x15bd:        // (blue)
5727       element = EL_CHAR_U;
5728       break;
5729
5730     case 0x15be:        // (blue)
5731       element = EL_CHAR_V;
5732       break;
5733
5734     case 0x15bf:        // (blue)
5735       element = EL_CHAR_W;
5736       break;
5737
5738     case 0x15c0:        // (blue)
5739       element = EL_CHAR_X;
5740       break;
5741
5742     case 0x15c1:        // (blue)
5743       element = EL_CHAR_Y;
5744       break;
5745
5746     case 0x15c2:        // (blue)
5747       element = EL_CHAR_Z;
5748       break;
5749
5750     case 0x15c3:        // (blue)
5751       element = EL_CHAR_AUMLAUT;
5752       break;
5753
5754     case 0x15c4:        // (blue)
5755       element = EL_CHAR_OUMLAUT;
5756       break;
5757
5758     case 0x15c5:        // (blue)
5759       element = EL_CHAR_UUMLAUT;
5760       break;
5761
5762     case 0x15c6:        // (blue)
5763       element = EL_CHAR_0;
5764       break;
5765
5766     case 0x15c7:        // (blue)
5767       element = EL_CHAR_1;
5768       break;
5769
5770     case 0x15c8:        // (blue)
5771       element = EL_CHAR_2;
5772       break;
5773
5774     case 0x15c9:        // (blue)
5775       element = EL_CHAR_3;
5776       break;
5777
5778     case 0x15ca:        // (blue)
5779       element = EL_CHAR_4;
5780       break;
5781
5782     case 0x15cb:        // (blue)
5783       element = EL_CHAR_5;
5784       break;
5785
5786     case 0x15cc:        // (blue)
5787       element = EL_CHAR_6;
5788       break;
5789
5790     case 0x15cd:        // (blue)
5791       element = EL_CHAR_7;
5792       break;
5793
5794     case 0x15ce:        // (blue)
5795       element = EL_CHAR_8;
5796       break;
5797
5798     case 0x15cf:        // (blue)
5799       element = EL_CHAR_9;
5800       break;
5801
5802     case 0x15d0:        // (blue)
5803       element = EL_CHAR_PERIOD;
5804       break;
5805
5806     case 0x15d1:        // (blue)
5807       element = EL_CHAR_EXCLAM;
5808       break;
5809
5810     case 0x15d2:        // (blue)
5811       element = EL_CHAR_COLON;
5812       break;
5813
5814     case 0x15d3:        // (blue)
5815       element = EL_CHAR_LESS;
5816       break;
5817
5818     case 0x15d4:        // (blue)
5819       element = EL_CHAR_GREATER;
5820       break;
5821
5822     case 0x15d5:        // (blue)
5823       element = EL_CHAR_QUESTION;
5824       break;
5825
5826     case 0x15d6:        // (blue)
5827       element = EL_CHAR_COPYRIGHT;
5828       break;
5829
5830     case 0x15d7:        // (blue)
5831       element = EL_CHAR_UP;
5832       break;
5833
5834     case 0x15d8:        // (blue)
5835       element = EL_CHAR_DOWN;
5836       break;
5837
5838     case 0x15d9:        // (blue)
5839       element = EL_CHAR_BUTTON;
5840       break;
5841
5842     case 0x15da:        // (blue)
5843       element = EL_CHAR_PLUS;
5844       break;
5845
5846     case 0x15db:        // (blue)
5847       element = EL_CHAR_MINUS;
5848       break;
5849
5850     case 0x15dc:        // (blue)
5851       element = EL_CHAR_APOSTROPHE;
5852       break;
5853
5854     case 0x15dd:        // (blue)
5855       element = EL_CHAR_PARENLEFT;
5856       break;
5857
5858     case 0x15de:        // (blue)
5859       element = EL_CHAR_PARENRIGHT;
5860       break;
5861
5862     case 0x15df:        // (green)
5863       element = EL_CHAR_A;
5864       break;
5865
5866     case 0x15e0:        // (green)
5867       element = EL_CHAR_B;
5868       break;
5869
5870     case 0x15e1:        // (green)
5871       element = EL_CHAR_C;
5872       break;
5873
5874     case 0x15e2:        // (green)
5875       element = EL_CHAR_D;
5876       break;
5877
5878     case 0x15e3:        // (green)
5879       element = EL_CHAR_E;
5880       break;
5881
5882     case 0x15e4:        // (green)
5883       element = EL_CHAR_F;
5884       break;
5885
5886     case 0x15e5:        // (green)
5887       element = EL_CHAR_G;
5888       break;
5889
5890     case 0x15e6:        // (green)
5891       element = EL_CHAR_H;
5892       break;
5893
5894     case 0x15e7:        // (green)
5895       element = EL_CHAR_I;
5896       break;
5897
5898     case 0x15e8:        // (green)
5899       element = EL_CHAR_J;
5900       break;
5901
5902     case 0x15e9:        // (green)
5903       element = EL_CHAR_K;
5904       break;
5905
5906     case 0x15ea:        // (green)
5907       element = EL_CHAR_L;
5908       break;
5909
5910     case 0x15eb:        // (green)
5911       element = EL_CHAR_M;
5912       break;
5913
5914     case 0x15ec:        // (green)
5915       element = EL_CHAR_N;
5916       break;
5917
5918     case 0x15ed:        // (green)
5919       element = EL_CHAR_O;
5920       break;
5921
5922     case 0x15ee:        // (green)
5923       element = EL_CHAR_P;
5924       break;
5925
5926     case 0x15ef:        // (green)
5927       element = EL_CHAR_Q;
5928       break;
5929
5930     case 0x15f0:        // (green)
5931       element = EL_CHAR_R;
5932       break;
5933
5934     case 0x15f1:        // (green)
5935       element = EL_CHAR_S;
5936       break;
5937
5938     case 0x15f2:        // (green)
5939       element = EL_CHAR_T;
5940       break;
5941
5942     case 0x15f3:        // (green)
5943       element = EL_CHAR_U;
5944       break;
5945
5946     case 0x15f4:        // (green)
5947       element = EL_CHAR_V;
5948       break;
5949
5950     case 0x15f5:        // (green)
5951       element = EL_CHAR_W;
5952       break;
5953
5954     case 0x15f6:        // (green)
5955       element = EL_CHAR_X;
5956       break;
5957
5958     case 0x15f7:        // (green)
5959       element = EL_CHAR_Y;
5960       break;
5961
5962     case 0x15f8:        // (green)
5963       element = EL_CHAR_Z;
5964       break;
5965
5966     case 0x15f9:        // (green)
5967       element = EL_CHAR_AUMLAUT;
5968       break;
5969
5970     case 0x15fa:        // (green)
5971       element = EL_CHAR_OUMLAUT;
5972       break;
5973
5974     case 0x15fb:        // (green)
5975       element = EL_CHAR_UUMLAUT;
5976       break;
5977
5978     case 0x15fc:        // (green)
5979       element = EL_CHAR_0;
5980       break;
5981
5982     case 0x15fd:        // (green)
5983       element = EL_CHAR_1;
5984       break;
5985
5986     case 0x15fe:        // (green)
5987       element = EL_CHAR_2;
5988       break;
5989
5990     case 0x15ff:        // (green)
5991       element = EL_CHAR_3;
5992       break;
5993
5994     case 0x1600:        // (green)
5995       element = EL_CHAR_4;
5996       break;
5997
5998     case 0x1601:        // (green)
5999       element = EL_CHAR_5;
6000       break;
6001
6002     case 0x1602:        // (green)
6003       element = EL_CHAR_6;
6004       break;
6005
6006     case 0x1603:        // (green)
6007       element = EL_CHAR_7;
6008       break;
6009
6010     case 0x1604:        // (green)
6011       element = EL_CHAR_8;
6012       break;
6013
6014     case 0x1605:        // (green)
6015       element = EL_CHAR_9;
6016       break;
6017
6018     case 0x1606:        // (green)
6019       element = EL_CHAR_PERIOD;
6020       break;
6021
6022     case 0x1607:        // (green)
6023       element = EL_CHAR_EXCLAM;
6024       break;
6025
6026     case 0x1608:        // (green)
6027       element = EL_CHAR_COLON;
6028       break;
6029
6030     case 0x1609:        // (green)
6031       element = EL_CHAR_LESS;
6032       break;
6033
6034     case 0x160a:        // (green)
6035       element = EL_CHAR_GREATER;
6036       break;
6037
6038     case 0x160b:        // (green)
6039       element = EL_CHAR_QUESTION;
6040       break;
6041
6042     case 0x160c:        // (green)
6043       element = EL_CHAR_COPYRIGHT;
6044       break;
6045
6046     case 0x160d:        // (green)
6047       element = EL_CHAR_UP;
6048       break;
6049
6050     case 0x160e:        // (green)
6051       element = EL_CHAR_DOWN;
6052       break;
6053
6054     case 0x160f:        // (green)
6055       element = EL_CHAR_BUTTON;
6056       break;
6057
6058     case 0x1610:        // (green)
6059       element = EL_CHAR_PLUS;
6060       break;
6061
6062     case 0x1611:        // (green)
6063       element = EL_CHAR_MINUS;
6064       break;
6065
6066     case 0x1612:        // (green)
6067       element = EL_CHAR_APOSTROPHE;
6068       break;
6069
6070     case 0x1613:        // (green)
6071       element = EL_CHAR_PARENLEFT;
6072       break;
6073
6074     case 0x1614:        // (green)
6075       element = EL_CHAR_PARENRIGHT;
6076       break;
6077
6078     case 0x1615:        // (blue steel)
6079       element = EL_STEEL_CHAR_A;
6080       break;
6081
6082     case 0x1616:        // (blue steel)
6083       element = EL_STEEL_CHAR_B;
6084       break;
6085
6086     case 0x1617:        // (blue steel)
6087       element = EL_STEEL_CHAR_C;
6088       break;
6089
6090     case 0x1618:        // (blue steel)
6091       element = EL_STEEL_CHAR_D;
6092       break;
6093
6094     case 0x1619:        // (blue steel)
6095       element = EL_STEEL_CHAR_E;
6096       break;
6097
6098     case 0x161a:        // (blue steel)
6099       element = EL_STEEL_CHAR_F;
6100       break;
6101
6102     case 0x161b:        // (blue steel)
6103       element = EL_STEEL_CHAR_G;
6104       break;
6105
6106     case 0x161c:        // (blue steel)
6107       element = EL_STEEL_CHAR_H;
6108       break;
6109
6110     case 0x161d:        // (blue steel)
6111       element = EL_STEEL_CHAR_I;
6112       break;
6113
6114     case 0x161e:        // (blue steel)
6115       element = EL_STEEL_CHAR_J;
6116       break;
6117
6118     case 0x161f:        // (blue steel)
6119       element = EL_STEEL_CHAR_K;
6120       break;
6121
6122     case 0x1620:        // (blue steel)
6123       element = EL_STEEL_CHAR_L;
6124       break;
6125
6126     case 0x1621:        // (blue steel)
6127       element = EL_STEEL_CHAR_M;
6128       break;
6129
6130     case 0x1622:        // (blue steel)
6131       element = EL_STEEL_CHAR_N;
6132       break;
6133
6134     case 0x1623:        // (blue steel)
6135       element = EL_STEEL_CHAR_O;
6136       break;
6137
6138     case 0x1624:        // (blue steel)
6139       element = EL_STEEL_CHAR_P;
6140       break;
6141
6142     case 0x1625:        // (blue steel)
6143       element = EL_STEEL_CHAR_Q;
6144       break;
6145
6146     case 0x1626:        // (blue steel)
6147       element = EL_STEEL_CHAR_R;
6148       break;
6149
6150     case 0x1627:        // (blue steel)
6151       element = EL_STEEL_CHAR_S;
6152       break;
6153
6154     case 0x1628:        // (blue steel)
6155       element = EL_STEEL_CHAR_T;
6156       break;
6157
6158     case 0x1629:        // (blue steel)
6159       element = EL_STEEL_CHAR_U;
6160       break;
6161
6162     case 0x162a:        // (blue steel)
6163       element = EL_STEEL_CHAR_V;
6164       break;
6165
6166     case 0x162b:        // (blue steel)
6167       element = EL_STEEL_CHAR_W;
6168       break;
6169
6170     case 0x162c:        // (blue steel)
6171       element = EL_STEEL_CHAR_X;
6172       break;
6173
6174     case 0x162d:        // (blue steel)
6175       element = EL_STEEL_CHAR_Y;
6176       break;
6177
6178     case 0x162e:        // (blue steel)
6179       element = EL_STEEL_CHAR_Z;
6180       break;
6181
6182     case 0x162f:        // (blue steel)
6183       element = EL_STEEL_CHAR_AUMLAUT;
6184       break;
6185
6186     case 0x1630:        // (blue steel)
6187       element = EL_STEEL_CHAR_OUMLAUT;
6188       break;
6189
6190     case 0x1631:        // (blue steel)
6191       element = EL_STEEL_CHAR_UUMLAUT;
6192       break;
6193
6194     case 0x1632:        // (blue steel)
6195       element = EL_STEEL_CHAR_0;
6196       break;
6197
6198     case 0x1633:        // (blue steel)
6199       element = EL_STEEL_CHAR_1;
6200       break;
6201
6202     case 0x1634:        // (blue steel)
6203       element = EL_STEEL_CHAR_2;
6204       break;
6205
6206     case 0x1635:        // (blue steel)
6207       element = EL_STEEL_CHAR_3;
6208       break;
6209
6210     case 0x1636:        // (blue steel)
6211       element = EL_STEEL_CHAR_4;
6212       break;
6213
6214     case 0x1637:        // (blue steel)
6215       element = EL_STEEL_CHAR_5;
6216       break;
6217
6218     case 0x1638:        // (blue steel)
6219       element = EL_STEEL_CHAR_6;
6220       break;
6221
6222     case 0x1639:        // (blue steel)
6223       element = EL_STEEL_CHAR_7;
6224       break;
6225
6226     case 0x163a:        // (blue steel)
6227       element = EL_STEEL_CHAR_8;
6228       break;
6229
6230     case 0x163b:        // (blue steel)
6231       element = EL_STEEL_CHAR_9;
6232       break;
6233
6234     case 0x163c:        // (blue steel)
6235       element = EL_STEEL_CHAR_PERIOD;
6236       break;
6237
6238     case 0x163d:        // (blue steel)
6239       element = EL_STEEL_CHAR_EXCLAM;
6240       break;
6241
6242     case 0x163e:        // (blue steel)
6243       element = EL_STEEL_CHAR_COLON;
6244       break;
6245
6246     case 0x163f:        // (blue steel)
6247       element = EL_STEEL_CHAR_LESS;
6248       break;
6249
6250     case 0x1640:        // (blue steel)
6251       element = EL_STEEL_CHAR_GREATER;
6252       break;
6253
6254     case 0x1641:        // (blue steel)
6255       element = EL_STEEL_CHAR_QUESTION;
6256       break;
6257
6258     case 0x1642:        // (blue steel)
6259       element = EL_STEEL_CHAR_COPYRIGHT;
6260       break;
6261
6262     case 0x1643:        // (blue steel)
6263       element = EL_STEEL_CHAR_UP;
6264       break;
6265
6266     case 0x1644:        // (blue steel)
6267       element = EL_STEEL_CHAR_DOWN;
6268       break;
6269
6270     case 0x1645:        // (blue steel)
6271       element = EL_STEEL_CHAR_BUTTON;
6272       break;
6273
6274     case 0x1646:        // (blue steel)
6275       element = EL_STEEL_CHAR_PLUS;
6276       break;
6277
6278     case 0x1647:        // (blue steel)
6279       element = EL_STEEL_CHAR_MINUS;
6280       break;
6281
6282     case 0x1648:        // (blue steel)
6283       element = EL_STEEL_CHAR_APOSTROPHE;
6284       break;
6285
6286     case 0x1649:        // (blue steel)
6287       element = EL_STEEL_CHAR_PARENLEFT;
6288       break;
6289
6290     case 0x164a:        // (blue steel)
6291       element = EL_STEEL_CHAR_PARENRIGHT;
6292       break;
6293
6294     case 0x164b:        // (green steel)
6295       element = EL_STEEL_CHAR_A;
6296       break;
6297
6298     case 0x164c:        // (green steel)
6299       element = EL_STEEL_CHAR_B;
6300       break;
6301
6302     case 0x164d:        // (green steel)
6303       element = EL_STEEL_CHAR_C;
6304       break;
6305
6306     case 0x164e:        // (green steel)
6307       element = EL_STEEL_CHAR_D;
6308       break;
6309
6310     case 0x164f:        // (green steel)
6311       element = EL_STEEL_CHAR_E;
6312       break;
6313
6314     case 0x1650:        // (green steel)
6315       element = EL_STEEL_CHAR_F;
6316       break;
6317
6318     case 0x1651:        // (green steel)
6319       element = EL_STEEL_CHAR_G;
6320       break;
6321
6322     case 0x1652:        // (green steel)
6323       element = EL_STEEL_CHAR_H;
6324       break;
6325
6326     case 0x1653:        // (green steel)
6327       element = EL_STEEL_CHAR_I;
6328       break;
6329
6330     case 0x1654:        // (green steel)
6331       element = EL_STEEL_CHAR_J;
6332       break;
6333
6334     case 0x1655:        // (green steel)
6335       element = EL_STEEL_CHAR_K;
6336       break;
6337
6338     case 0x1656:        // (green steel)
6339       element = EL_STEEL_CHAR_L;
6340       break;
6341
6342     case 0x1657:        // (green steel)
6343       element = EL_STEEL_CHAR_M;
6344       break;
6345
6346     case 0x1658:        // (green steel)
6347       element = EL_STEEL_CHAR_N;
6348       break;
6349
6350     case 0x1659:        // (green steel)
6351       element = EL_STEEL_CHAR_O;
6352       break;
6353
6354     case 0x165a:        // (green steel)
6355       element = EL_STEEL_CHAR_P;
6356       break;
6357
6358     case 0x165b:        // (green steel)
6359       element = EL_STEEL_CHAR_Q;
6360       break;
6361
6362     case 0x165c:        // (green steel)
6363       element = EL_STEEL_CHAR_R;
6364       break;
6365
6366     case 0x165d:        // (green steel)
6367       element = EL_STEEL_CHAR_S;
6368       break;
6369
6370     case 0x165e:        // (green steel)
6371       element = EL_STEEL_CHAR_T;
6372       break;
6373
6374     case 0x165f:        // (green steel)
6375       element = EL_STEEL_CHAR_U;
6376       break;
6377
6378     case 0x1660:        // (green steel)
6379       element = EL_STEEL_CHAR_V;
6380       break;
6381
6382     case 0x1661:        // (green steel)
6383       element = EL_STEEL_CHAR_W;
6384       break;
6385
6386     case 0x1662:        // (green steel)
6387       element = EL_STEEL_CHAR_X;
6388       break;
6389
6390     case 0x1663:        // (green steel)
6391       element = EL_STEEL_CHAR_Y;
6392       break;
6393
6394     case 0x1664:        // (green steel)
6395       element = EL_STEEL_CHAR_Z;
6396       break;
6397
6398     case 0x1665:        // (green steel)
6399       element = EL_STEEL_CHAR_AUMLAUT;
6400       break;
6401
6402     case 0x1666:        // (green steel)
6403       element = EL_STEEL_CHAR_OUMLAUT;
6404       break;
6405
6406     case 0x1667:        // (green steel)
6407       element = EL_STEEL_CHAR_UUMLAUT;
6408       break;
6409
6410     case 0x1668:        // (green steel)
6411       element = EL_STEEL_CHAR_0;
6412       break;
6413
6414     case 0x1669:        // (green steel)
6415       element = EL_STEEL_CHAR_1;
6416       break;
6417
6418     case 0x166a:        // (green steel)
6419       element = EL_STEEL_CHAR_2;
6420       break;
6421
6422     case 0x166b:        // (green steel)
6423       element = EL_STEEL_CHAR_3;
6424       break;
6425
6426     case 0x166c:        // (green steel)
6427       element = EL_STEEL_CHAR_4;
6428       break;
6429
6430     case 0x166d:        // (green steel)
6431       element = EL_STEEL_CHAR_5;
6432       break;
6433
6434     case 0x166e:        // (green steel)
6435       element = EL_STEEL_CHAR_6;
6436       break;
6437
6438     case 0x166f:        // (green steel)
6439       element = EL_STEEL_CHAR_7;
6440       break;
6441
6442     case 0x1670:        // (green steel)
6443       element = EL_STEEL_CHAR_8;
6444       break;
6445
6446     case 0x1671:        // (green steel)
6447       element = EL_STEEL_CHAR_9;
6448       break;
6449
6450     case 0x1672:        // (green steel)
6451       element = EL_STEEL_CHAR_PERIOD;
6452       break;
6453
6454     case 0x1673:        // (green steel)
6455       element = EL_STEEL_CHAR_EXCLAM;
6456       break;
6457
6458     case 0x1674:        // (green steel)
6459       element = EL_STEEL_CHAR_COLON;
6460       break;
6461
6462     case 0x1675:        // (green steel)
6463       element = EL_STEEL_CHAR_LESS;
6464       break;
6465
6466     case 0x1676:        // (green steel)
6467       element = EL_STEEL_CHAR_GREATER;
6468       break;
6469
6470     case 0x1677:        // (green steel)
6471       element = EL_STEEL_CHAR_QUESTION;
6472       break;
6473
6474     case 0x1678:        // (green steel)
6475       element = EL_STEEL_CHAR_COPYRIGHT;
6476       break;
6477
6478     case 0x1679:        // (green steel)
6479       element = EL_STEEL_CHAR_UP;
6480       break;
6481
6482     case 0x167a:        // (green steel)
6483       element = EL_STEEL_CHAR_DOWN;
6484       break;
6485
6486     case 0x167b:        // (green steel)
6487       element = EL_STEEL_CHAR_BUTTON;
6488       break;
6489
6490     case 0x167c:        // (green steel)
6491       element = EL_STEEL_CHAR_PLUS;
6492       break;
6493
6494     case 0x167d:        // (green steel)
6495       element = EL_STEEL_CHAR_MINUS;
6496       break;
6497
6498     case 0x167e:        // (green steel)
6499       element = EL_STEEL_CHAR_APOSTROPHE;
6500       break;
6501
6502     case 0x167f:        // (green steel)
6503       element = EL_STEEL_CHAR_PARENLEFT;
6504       break;
6505
6506     case 0x1680:        // (green steel)
6507       element = EL_STEEL_CHAR_PARENRIGHT;
6508       break;
6509
6510     case 0x1681:        // gate (red)
6511       element = EL_EM_GATE_1;
6512       break;
6513
6514     case 0x1682:        // secret gate (red)
6515       element = EL_EM_GATE_1_GRAY;
6516       break;
6517
6518     case 0x1683:        // gate (yellow)
6519       element = EL_EM_GATE_2;
6520       break;
6521
6522     case 0x1684:        // secret gate (yellow)
6523       element = EL_EM_GATE_2_GRAY;
6524       break;
6525
6526     case 0x1685:        // gate (blue)
6527       element = EL_EM_GATE_4;
6528       break;
6529
6530     case 0x1686:        // secret gate (blue)
6531       element = EL_EM_GATE_4_GRAY;
6532       break;
6533
6534     case 0x1687:        // gate (green)
6535       element = EL_EM_GATE_3;
6536       break;
6537
6538     case 0x1688:        // secret gate (green)
6539       element = EL_EM_GATE_3_GRAY;
6540       break;
6541
6542     case 0x1689:        // gate (white)
6543       element = EL_DC_GATE_WHITE;
6544       break;
6545
6546     case 0x168a:        // secret gate (white)
6547       element = EL_DC_GATE_WHITE_GRAY;
6548       break;
6549
6550     case 0x168b:        // secret gate (no key)
6551       element = EL_DC_GATE_FAKE_GRAY;
6552       break;
6553
6554     case 0x168c:
6555       element = EL_ROBOT_WHEEL;
6556       break;
6557
6558     case 0x168d:
6559       element = EL_DC_TIMEGATE_SWITCH;
6560       break;
6561
6562     case 0x168e:
6563       element = EL_ACID_POOL_BOTTOM;
6564       break;
6565
6566     case 0x168f:
6567       element = EL_ACID_POOL_TOPLEFT;
6568       break;
6569
6570     case 0x1690:
6571       element = EL_ACID_POOL_TOPRIGHT;
6572       break;
6573
6574     case 0x1691:
6575       element = EL_ACID_POOL_BOTTOMLEFT;
6576       break;
6577
6578     case 0x1692:
6579       element = EL_ACID_POOL_BOTTOMRIGHT;
6580       break;
6581
6582     case 0x1693:
6583       element = EL_STEELWALL;
6584       break;
6585
6586     case 0x1694:
6587       element = EL_STEELWALL_SLIPPERY;
6588       break;
6589
6590     case 0x1695:        // steel wall (not round)
6591       element = EL_STEELWALL;
6592       break;
6593
6594     case 0x1696:        // steel wall (left)
6595       element = EL_DC_STEELWALL_1_LEFT;
6596       break;
6597
6598     case 0x1697:        // steel wall (bottom)
6599       element = EL_DC_STEELWALL_1_BOTTOM;
6600       break;
6601
6602     case 0x1698:        // steel wall (right)
6603       element = EL_DC_STEELWALL_1_RIGHT;
6604       break;
6605
6606     case 0x1699:        // steel wall (top)
6607       element = EL_DC_STEELWALL_1_TOP;
6608       break;
6609
6610     case 0x169a:        // steel wall (left/bottom)
6611       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6612       break;
6613
6614     case 0x169b:        // steel wall (right/bottom)
6615       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6616       break;
6617
6618     case 0x169c:        // steel wall (right/top)
6619       element = EL_DC_STEELWALL_1_TOPRIGHT;
6620       break;
6621
6622     case 0x169d:        // steel wall (left/top)
6623       element = EL_DC_STEELWALL_1_TOPLEFT;
6624       break;
6625
6626     case 0x169e:        // steel wall (right/bottom small)
6627       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6628       break;
6629
6630     case 0x169f:        // steel wall (left/bottom small)
6631       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6632       break;
6633
6634     case 0x16a0:        // steel wall (right/top small)
6635       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6636       break;
6637
6638     case 0x16a1:        // steel wall (left/top small)
6639       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6640       break;
6641
6642     case 0x16a2:        // steel wall (left/right)
6643       element = EL_DC_STEELWALL_1_VERTICAL;
6644       break;
6645
6646     case 0x16a3:        // steel wall (top/bottom)
6647       element = EL_DC_STEELWALL_1_HORIZONTAL;
6648       break;
6649
6650     case 0x16a4:        // steel wall 2 (left end)
6651       element = EL_DC_STEELWALL_2_LEFT;
6652       break;
6653
6654     case 0x16a5:        // steel wall 2 (right end)
6655       element = EL_DC_STEELWALL_2_RIGHT;
6656       break;
6657
6658     case 0x16a6:        // steel wall 2 (top end)
6659       element = EL_DC_STEELWALL_2_TOP;
6660       break;
6661
6662     case 0x16a7:        // steel wall 2 (bottom end)
6663       element = EL_DC_STEELWALL_2_BOTTOM;
6664       break;
6665
6666     case 0x16a8:        // steel wall 2 (left/right)
6667       element = EL_DC_STEELWALL_2_HORIZONTAL;
6668       break;
6669
6670     case 0x16a9:        // steel wall 2 (up/down)
6671       element = EL_DC_STEELWALL_2_VERTICAL;
6672       break;
6673
6674     case 0x16aa:        // steel wall 2 (mid)
6675       element = EL_DC_STEELWALL_2_MIDDLE;
6676       break;
6677
6678     case 0x16ab:
6679       element = EL_SIGN_EXCLAMATION;
6680       break;
6681
6682     case 0x16ac:
6683       element = EL_SIGN_RADIOACTIVITY;
6684       break;
6685
6686     case 0x16ad:
6687       element = EL_SIGN_STOP;
6688       break;
6689
6690     case 0x16ae:
6691       element = EL_SIGN_WHEELCHAIR;
6692       break;
6693
6694     case 0x16af:
6695       element = EL_SIGN_PARKING;
6696       break;
6697
6698     case 0x16b0:
6699       element = EL_SIGN_NO_ENTRY;
6700       break;
6701
6702     case 0x16b1:
6703       element = EL_SIGN_HEART;
6704       break;
6705
6706     case 0x16b2:
6707       element = EL_SIGN_GIVE_WAY;
6708       break;
6709
6710     case 0x16b3:
6711       element = EL_SIGN_ENTRY_FORBIDDEN;
6712       break;
6713
6714     case 0x16b4:
6715       element = EL_SIGN_EMERGENCY_EXIT;
6716       break;
6717
6718     case 0x16b5:
6719       element = EL_SIGN_YIN_YANG;
6720       break;
6721
6722     case 0x16b6:
6723       element = EL_WALL_EMERALD;
6724       break;
6725
6726     case 0x16b7:
6727       element = EL_WALL_DIAMOND;
6728       break;
6729
6730     case 0x16b8:
6731       element = EL_WALL_PEARL;
6732       break;
6733
6734     case 0x16b9:
6735       element = EL_WALL_CRYSTAL;
6736       break;
6737
6738     case 0x16ba:
6739       element = EL_INVISIBLE_WALL;
6740       break;
6741
6742     case 0x16bb:
6743       element = EL_INVISIBLE_STEELWALL;
6744       break;
6745
6746       // 0x16bc - 0x16cb:
6747       // EL_INVISIBLE_SAND
6748
6749     case 0x16cc:
6750       element = EL_LIGHT_SWITCH;
6751       break;
6752
6753     case 0x16cd:
6754       element = EL_ENVELOPE_1;
6755       break;
6756
6757     default:
6758       if (element >= 0x0117 && element <= 0x036e)       // (?)
6759         element = EL_DIAMOND;
6760       else if (element >= 0x042d && element <= 0x0684)  // (?)
6761         element = EL_EMERALD;
6762       else if (element >= 0x157c && element <= 0x158b)
6763         element = EL_SAND;
6764       else if (element >= 0x1590 && element <= 0x159f)
6765         element = EL_DC_LANDMINE;
6766       else if (element >= 0x16bc && element <= 0x16cb)
6767         element = EL_INVISIBLE_SAND;
6768       else
6769       {
6770         Warn("unknown Diamond Caves element 0x%04x", element);
6771
6772         element = EL_UNKNOWN;
6773       }
6774       break;
6775   }
6776
6777   return getMappedElement(element);
6778 }
6779
6780 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6781 {
6782   byte header[DC_LEVEL_HEADER_SIZE];
6783   int envelope_size;
6784   int envelope_header_pos = 62;
6785   int envelope_content_pos = 94;
6786   int level_name_pos = 251;
6787   int level_author_pos = 292;
6788   int envelope_header_len;
6789   int envelope_content_len;
6790   int level_name_len;
6791   int level_author_len;
6792   int fieldx, fieldy;
6793   int num_yamyam_contents;
6794   int i, x, y;
6795
6796   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6797
6798   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6799   {
6800     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6801
6802     header[i * 2 + 0] = header_word >> 8;
6803     header[i * 2 + 1] = header_word & 0xff;
6804   }
6805
6806   // read some values from level header to check level decoding integrity
6807   fieldx = header[6] | (header[7] << 8);
6808   fieldy = header[8] | (header[9] << 8);
6809   num_yamyam_contents = header[60] | (header[61] << 8);
6810
6811   // do some simple sanity checks to ensure that level was correctly decoded
6812   if (fieldx < 1 || fieldx > 256 ||
6813       fieldy < 1 || fieldy > 256 ||
6814       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6815   {
6816     level->no_valid_file = TRUE;
6817
6818     Warn("cannot decode level from stream -- using empty level");
6819
6820     return;
6821   }
6822
6823   // maximum envelope header size is 31 bytes
6824   envelope_header_len   = header[envelope_header_pos];
6825   // maximum envelope content size is 110 (156?) bytes
6826   envelope_content_len  = header[envelope_content_pos];
6827
6828   // maximum level title size is 40 bytes
6829   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6830   // maximum level author size is 30 (51?) bytes
6831   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6832
6833   envelope_size = 0;
6834
6835   for (i = 0; i < envelope_header_len; i++)
6836     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6837       level->envelope[0].text[envelope_size++] =
6838         header[envelope_header_pos + 1 + i];
6839
6840   if (envelope_header_len > 0 && envelope_content_len > 0)
6841   {
6842     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6843       level->envelope[0].text[envelope_size++] = '\n';
6844     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6845       level->envelope[0].text[envelope_size++] = '\n';
6846   }
6847
6848   for (i = 0; i < envelope_content_len; i++)
6849     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6850       level->envelope[0].text[envelope_size++] =
6851         header[envelope_content_pos + 1 + i];
6852
6853   level->envelope[0].text[envelope_size] = '\0';
6854
6855   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6856   level->envelope[0].ysize = 10;
6857   level->envelope[0].autowrap = TRUE;
6858   level->envelope[0].centered = TRUE;
6859
6860   for (i = 0; i < level_name_len; i++)
6861     level->name[i] = header[level_name_pos + 1 + i];
6862   level->name[level_name_len] = '\0';
6863
6864   for (i = 0; i < level_author_len; i++)
6865     level->author[i] = header[level_author_pos + 1 + i];
6866   level->author[level_author_len] = '\0';
6867
6868   num_yamyam_contents = header[60] | (header[61] << 8);
6869   level->num_yamyam_contents =
6870     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6871
6872   for (i = 0; i < num_yamyam_contents; i++)
6873   {
6874     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6875     {
6876       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6877       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6878
6879       if (i < MAX_ELEMENT_CONTENTS)
6880         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6881     }
6882   }
6883
6884   fieldx = header[6] | (header[7] << 8);
6885   fieldy = header[8] | (header[9] << 8);
6886   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6887   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6888
6889   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6890   {
6891     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6892     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6893
6894     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6895       level->field[x][y] = getMappedElement_DC(element_dc);
6896   }
6897
6898   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6899   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6900   level->field[x][y] = EL_PLAYER_1;
6901
6902   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6903   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6904   level->field[x][y] = EL_PLAYER_2;
6905
6906   level->gems_needed            = header[18] | (header[19] << 8);
6907
6908   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6909   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6910   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6911   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6912   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6913   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6914   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6915   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6916   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6917   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6918   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6919   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6920
6921   level->time                   = header[44] | (header[45] << 8);
6922
6923   level->amoeba_speed           = header[46] | (header[47] << 8);
6924   level->time_light             = header[48] | (header[49] << 8);
6925   level->time_timegate          = header[50] | (header[51] << 8);
6926   level->time_wheel             = header[52] | (header[53] << 8);
6927   level->time_magic_wall        = header[54] | (header[55] << 8);
6928   level->extra_time             = header[56] | (header[57] << 8);
6929   level->shield_normal_time     = header[58] | (header[59] << 8);
6930
6931   // shield and extra time elements do not have a score
6932   level->score[SC_SHIELD]       = 0;
6933   level->extra_time_score       = 0;
6934
6935   // set time for normal and deadly shields to the same value
6936   level->shield_deadly_time     = level->shield_normal_time;
6937
6938   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6939   // can slip down from flat walls, like normal walls and steel walls
6940   level->em_slippery_gems = TRUE;
6941
6942   // time score is counted for each 10 seconds left in Diamond Caves levels
6943   level->time_score_base = 10;
6944 }
6945
6946 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6947                                      struct LevelFileInfo *level_file_info,
6948                                      boolean level_info_only)
6949 {
6950   char *filename = level_file_info->filename;
6951   File *file;
6952   int num_magic_bytes = 8;
6953   char magic_bytes[num_magic_bytes + 1];
6954   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6955
6956   if (!(file = openFile(filename, MODE_READ)))
6957   {
6958     level->no_valid_file = TRUE;
6959
6960     if (!level_info_only)
6961       Warn("cannot read level '%s' -- using empty level", filename);
6962
6963     return;
6964   }
6965
6966   // fseek(file, 0x0000, SEEK_SET);
6967
6968   if (level_file_info->packed)
6969   {
6970     // read "magic bytes" from start of file
6971     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6972       magic_bytes[0] = '\0';
6973
6974     // check "magic bytes" for correct file format
6975     if (!strPrefix(magic_bytes, "DC2"))
6976     {
6977       level->no_valid_file = TRUE;
6978
6979       Warn("unknown DC level file '%s' -- using empty level", filename);
6980
6981       return;
6982     }
6983
6984     if (strPrefix(magic_bytes, "DC2Win95") ||
6985         strPrefix(magic_bytes, "DC2Win98"))
6986     {
6987       int position_first_level = 0x00fa;
6988       int extra_bytes = 4;
6989       int skip_bytes;
6990
6991       // advance file stream to first level inside the level package
6992       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6993
6994       // each block of level data is followed by block of non-level data
6995       num_levels_to_skip *= 2;
6996
6997       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6998       while (num_levels_to_skip >= 0)
6999       {
7000         // advance file stream to next level inside the level package
7001         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
7002         {
7003           level->no_valid_file = TRUE;
7004
7005           Warn("cannot fseek in file '%s' -- using empty level", filename);
7006
7007           return;
7008         }
7009
7010         // skip apparently unused extra bytes following each level
7011         ReadUnusedBytesFromFile(file, extra_bytes);
7012
7013         // read size of next level in level package
7014         skip_bytes = getFile32BitLE(file);
7015
7016         num_levels_to_skip--;
7017       }
7018     }
7019     else
7020     {
7021       level->no_valid_file = TRUE;
7022
7023       Warn("unknown DC2 level file '%s' -- using empty level", filename);
7024
7025       return;
7026     }
7027   }
7028
7029   LoadLevelFromFileStream_DC(file, level);
7030
7031   closeFile(file);
7032 }
7033
7034
7035 // ----------------------------------------------------------------------------
7036 // functions for loading SB level
7037 // ----------------------------------------------------------------------------
7038
7039 int getMappedElement_SB(int element_ascii, boolean use_ces)
7040 {
7041   static struct
7042   {
7043     int ascii;
7044     int sb;
7045     int ce;
7046   }
7047   sb_element_mapping[] =
7048   {
7049     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
7050     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
7051     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
7052     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
7053     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
7054     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
7055     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
7056     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
7057
7058     { 0,   -1,                      -1          },
7059   };
7060
7061   int i;
7062
7063   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
7064     if (element_ascii == sb_element_mapping[i].ascii)
7065       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
7066
7067   return EL_UNDEFINED;
7068 }
7069
7070 static void SetLevelSettings_SB(struct LevelInfo *level)
7071 {
7072   // time settings
7073   level->time = 0;
7074   level->use_step_counter = TRUE;
7075
7076   // score settings
7077   level->score[SC_TIME_BONUS] = 0;
7078   level->time_score_base = 1;
7079   level->rate_time_over_score = TRUE;
7080
7081   // game settings
7082   level->auto_exit_sokoban = TRUE;
7083 }
7084
7085 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7086                                      struct LevelFileInfo *level_file_info,
7087                                      boolean level_info_only)
7088 {
7089   char *filename = level_file_info->filename;
7090   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7091   char last_comment[MAX_LINE_LEN];
7092   char level_name[MAX_LINE_LEN];
7093   char *line_ptr;
7094   File *file;
7095   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7096   boolean read_continued_line = FALSE;
7097   boolean reading_playfield = FALSE;
7098   boolean got_valid_playfield_line = FALSE;
7099   boolean invalid_playfield_char = FALSE;
7100   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7101   int file_level_nr = 0;
7102   int x = 0, y = 0;             // initialized to make compilers happy
7103
7104   last_comment[0] = '\0';
7105   level_name[0] = '\0';
7106
7107   if (!(file = openFile(filename, MODE_READ)))
7108   {
7109     level->no_valid_file = TRUE;
7110
7111     if (!level_info_only)
7112       Warn("cannot read level '%s' -- using empty level", filename);
7113
7114     return;
7115   }
7116
7117   while (!checkEndOfFile(file))
7118   {
7119     // level successfully read, but next level may follow here
7120     if (!got_valid_playfield_line && reading_playfield)
7121     {
7122       // read playfield from single level file -- skip remaining file
7123       if (!level_file_info->packed)
7124         break;
7125
7126       if (file_level_nr >= num_levels_to_skip)
7127         break;
7128
7129       file_level_nr++;
7130
7131       last_comment[0] = '\0';
7132       level_name[0] = '\0';
7133
7134       reading_playfield = FALSE;
7135     }
7136
7137     got_valid_playfield_line = FALSE;
7138
7139     // read next line of input file
7140     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7141       break;
7142
7143     // cut trailing line break (this can be newline and/or carriage return)
7144     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7145       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7146         *line_ptr = '\0';
7147
7148     // copy raw input line for later use (mainly debugging output)
7149     strcpy(line_raw, line);
7150
7151     if (read_continued_line)
7152     {
7153       // append new line to existing line, if there is enough space
7154       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7155         strcat(previous_line, line_ptr);
7156
7157       strcpy(line, previous_line);      // copy storage buffer to line
7158
7159       read_continued_line = FALSE;
7160     }
7161
7162     // if the last character is '\', continue at next line
7163     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7164     {
7165       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7166       strcpy(previous_line, line);      // copy line to storage buffer
7167
7168       read_continued_line = TRUE;
7169
7170       continue;
7171     }
7172
7173     // skip empty lines
7174     if (line[0] == '\0')
7175       continue;
7176
7177     // extract comment text from comment line
7178     if (line[0] == ';')
7179     {
7180       for (line_ptr = line; *line_ptr; line_ptr++)
7181         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7182           break;
7183
7184       strcpy(last_comment, line_ptr);
7185
7186       continue;
7187     }
7188
7189     // extract level title text from line containing level title
7190     if (line[0] == '\'')
7191     {
7192       strcpy(level_name, &line[1]);
7193
7194       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7195         level_name[strlen(level_name) - 1] = '\0';
7196
7197       continue;
7198     }
7199
7200     // skip lines containing only spaces (or empty lines)
7201     for (line_ptr = line; *line_ptr; line_ptr++)
7202       if (*line_ptr != ' ')
7203         break;
7204     if (*line_ptr == '\0')
7205       continue;
7206
7207     // at this point, we have found a line containing part of a playfield
7208
7209     got_valid_playfield_line = TRUE;
7210
7211     if (!reading_playfield)
7212     {
7213       reading_playfield = TRUE;
7214       invalid_playfield_char = FALSE;
7215
7216       for (x = 0; x < MAX_LEV_FIELDX; x++)
7217         for (y = 0; y < MAX_LEV_FIELDY; y++)
7218           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7219
7220       level->fieldx = 0;
7221       level->fieldy = 0;
7222
7223       // start with topmost tile row
7224       y = 0;
7225     }
7226
7227     // skip playfield line if larger row than allowed
7228     if (y >= MAX_LEV_FIELDY)
7229       continue;
7230
7231     // start with leftmost tile column
7232     x = 0;
7233
7234     // read playfield elements from line
7235     for (line_ptr = line; *line_ptr; line_ptr++)
7236     {
7237       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7238
7239       // stop parsing playfield line if larger column than allowed
7240       if (x >= MAX_LEV_FIELDX)
7241         break;
7242
7243       if (mapped_sb_element == EL_UNDEFINED)
7244       {
7245         invalid_playfield_char = TRUE;
7246
7247         break;
7248       }
7249
7250       level->field[x][y] = mapped_sb_element;
7251
7252       // continue with next tile column
7253       x++;
7254
7255       level->fieldx = MAX(x, level->fieldx);
7256     }
7257
7258     if (invalid_playfield_char)
7259     {
7260       // if first playfield line, treat invalid lines as comment lines
7261       if (y == 0)
7262         reading_playfield = FALSE;
7263
7264       continue;
7265     }
7266
7267     // continue with next tile row
7268     y++;
7269   }
7270
7271   closeFile(file);
7272
7273   level->fieldy = y;
7274
7275   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7276   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7277
7278   if (!reading_playfield)
7279   {
7280     level->no_valid_file = TRUE;
7281
7282     Warn("cannot read level '%s' -- using empty level", filename);
7283
7284     return;
7285   }
7286
7287   if (*level_name != '\0')
7288   {
7289     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7290     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7291   }
7292   else if (*last_comment != '\0')
7293   {
7294     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7295     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7296   }
7297   else
7298   {
7299     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7300   }
7301
7302   // set all empty fields beyond the border walls to invisible steel wall
7303   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7304   {
7305     if ((x == 0 || x == level->fieldx - 1 ||
7306          y == 0 || y == level->fieldy - 1) &&
7307         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7308       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7309                      level->field, level->fieldx, level->fieldy);
7310   }
7311
7312   // set special level settings for Sokoban levels
7313   SetLevelSettings_SB(level);
7314
7315   if (load_xsb_to_ces)
7316   {
7317     // special global settings can now be set in level template
7318     level->use_custom_template = TRUE;
7319   }
7320 }
7321
7322
7323 // -------------------------------------------------------------------------
7324 // functions for handling native levels
7325 // -------------------------------------------------------------------------
7326
7327 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7328                                      struct LevelFileInfo *level_file_info,
7329                                      boolean level_info_only)
7330 {
7331   int pos = 0;
7332
7333   // determine position of requested level inside level package
7334   if (level_file_info->packed)
7335     pos = level_file_info->nr - leveldir_current->first_level;
7336
7337   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7338     level->no_valid_file = TRUE;
7339 }
7340
7341 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7342                                      struct LevelFileInfo *level_file_info,
7343                                      boolean level_info_only)
7344 {
7345   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7346     level->no_valid_file = TRUE;
7347 }
7348
7349 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7350                                      struct LevelFileInfo *level_file_info,
7351                                      boolean level_info_only)
7352 {
7353   int pos = 0;
7354
7355   // determine position of requested level inside level package
7356   if (level_file_info->packed)
7357     pos = level_file_info->nr - leveldir_current->first_level;
7358
7359   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7360     level->no_valid_file = TRUE;
7361 }
7362
7363 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7364                                      struct LevelFileInfo *level_file_info,
7365                                      boolean level_info_only)
7366 {
7367   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7368     level->no_valid_file = TRUE;
7369 }
7370
7371 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7372 {
7373   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7374     CopyNativeLevel_RND_to_BD(level);
7375   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7376     CopyNativeLevel_RND_to_EM(level);
7377   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7378     CopyNativeLevel_RND_to_SP(level);
7379   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7380     CopyNativeLevel_RND_to_MM(level);
7381 }
7382
7383 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7384 {
7385   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7386     CopyNativeLevel_BD_to_RND(level);
7387   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7388     CopyNativeLevel_EM_to_RND(level);
7389   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7390     CopyNativeLevel_SP_to_RND(level);
7391   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7392     CopyNativeLevel_MM_to_RND(level);
7393 }
7394
7395 void SaveNativeLevel(struct LevelInfo *level)
7396 {
7397   // saving native level files only supported for some game engines
7398   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7399       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7400     return;
7401
7402   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7403                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7404   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7405   char *filename = getLevelFilenameFromBasename(basename);
7406
7407   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7408     return;
7409
7410   boolean success = FALSE;
7411
7412   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7413   {
7414     CopyNativeLevel_RND_to_BD(level);
7415     // CopyNativeTape_RND_to_BD(level);
7416
7417     success = SaveNativeLevel_BD(filename);
7418   }
7419   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7420   {
7421     CopyNativeLevel_RND_to_SP(level);
7422     CopyNativeTape_RND_to_SP(level);
7423
7424     success = SaveNativeLevel_SP(filename);
7425   }
7426
7427   if (success)
7428     Request("Native level file saved!", REQ_CONFIRM);
7429   else
7430     Request("Failed to save native level file!", REQ_CONFIRM);
7431 }
7432
7433
7434 // ----------------------------------------------------------------------------
7435 // functions for loading generic level
7436 // ----------------------------------------------------------------------------
7437
7438 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7439                                   struct LevelFileInfo *level_file_info,
7440                                   boolean level_info_only)
7441 {
7442   // always start with reliable default values
7443   setLevelInfoToDefaults(level, level_info_only, TRUE);
7444
7445   switch (level_file_info->type)
7446   {
7447     case LEVEL_FILE_TYPE_RND:
7448       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7449       break;
7450
7451     case LEVEL_FILE_TYPE_BD:
7452       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7453       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7454       break;
7455
7456     case LEVEL_FILE_TYPE_EM:
7457       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7458       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7459       break;
7460
7461     case LEVEL_FILE_TYPE_SP:
7462       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7463       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7464       break;
7465
7466     case LEVEL_FILE_TYPE_MM:
7467       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7468       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7469       break;
7470
7471     case LEVEL_FILE_TYPE_DC:
7472       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7473       break;
7474
7475     case LEVEL_FILE_TYPE_SB:
7476       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7477       break;
7478
7479     default:
7480       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7481       break;
7482   }
7483
7484   // if level file is invalid, restore level structure to default values
7485   if (level->no_valid_file)
7486     setLevelInfoToDefaults(level, level_info_only, FALSE);
7487
7488   if (check_special_flags("use_native_bd_game_engine"))
7489     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7490
7491   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7492     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7493
7494   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7495     CopyNativeLevel_Native_to_RND(level);
7496 }
7497
7498 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7499 {
7500   static struct LevelFileInfo level_file_info;
7501
7502   // always start with reliable default values
7503   setFileInfoToDefaults(&level_file_info);
7504
7505   level_file_info.nr = 0;                       // unknown level number
7506   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7507
7508   setString(&level_file_info.filename, filename);
7509
7510   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7511 }
7512
7513 static void LoadLevel_InitVersion(struct LevelInfo *level)
7514 {
7515   int i, j;
7516
7517   if (leveldir_current == NULL)         // only when dumping level
7518     return;
7519
7520   // all engine modifications also valid for levels which use latest engine
7521   if (level->game_version < VERSION_IDENT(3,2,0,5))
7522   {
7523     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7524     level->time_score_base = 10;
7525   }
7526
7527   if (leveldir_current->latest_engine)
7528   {
7529     // ---------- use latest game engine --------------------------------------
7530
7531     /* For all levels which are forced to use the latest game engine version
7532        (normally all but user contributed, private and undefined levels), set
7533        the game engine version to the actual version; this allows for actual
7534        corrections in the game engine to take effect for existing, converted
7535        levels (from "classic" or other existing games) to make the emulation
7536        of the corresponding game more accurate, while (hopefully) not breaking
7537        existing levels created from other players. */
7538
7539     level->game_version = GAME_VERSION_ACTUAL;
7540
7541     /* Set special EM style gems behaviour: EM style gems slip down from
7542        normal, steel and growing wall. As this is a more fundamental change,
7543        it seems better to set the default behaviour to "off" (as it is more
7544        natural) and make it configurable in the level editor (as a property
7545        of gem style elements). Already existing converted levels (neither
7546        private nor contributed levels) are changed to the new behaviour. */
7547
7548     if (level->file_version < FILE_VERSION_2_0)
7549       level->em_slippery_gems = TRUE;
7550
7551     return;
7552   }
7553
7554   // ---------- use game engine the level was created with --------------------
7555
7556   /* For all levels which are not forced to use the latest game engine
7557      version (normally user contributed, private and undefined levels),
7558      use the version of the game engine the levels were created for.
7559
7560      Since 2.0.1, the game engine version is now directly stored
7561      in the level file (chunk "VERS"), so there is no need anymore
7562      to set the game version from the file version (except for old,
7563      pre-2.0 levels, where the game version is still taken from the
7564      file format version used to store the level -- see above). */
7565
7566   // player was faster than enemies in 1.0.0 and before
7567   if (level->file_version == FILE_VERSION_1_0)
7568     for (i = 0; i < MAX_PLAYERS; i++)
7569       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7570
7571   // default behaviour for EM style gems was "slippery" only in 2.0.1
7572   if (level->game_version == VERSION_IDENT(2,0,1,0))
7573     level->em_slippery_gems = TRUE;
7574
7575   // springs could be pushed over pits before (pre-release version) 2.2.0
7576   if (level->game_version < VERSION_IDENT(2,2,0,0))
7577     level->use_spring_bug = TRUE;
7578
7579   if (level->game_version < VERSION_IDENT(3,2,0,5))
7580   {
7581     // time orb caused limited time in endless time levels before 3.2.0-5
7582     level->use_time_orb_bug = TRUE;
7583
7584     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7585     level->block_snap_field = FALSE;
7586
7587     // extra time score was same value as time left score before 3.2.0-5
7588     level->extra_time_score = level->score[SC_TIME_BONUS];
7589   }
7590
7591   if (level->game_version < VERSION_IDENT(3,2,0,7))
7592   {
7593     // default behaviour for snapping was "not continuous" before 3.2.0-7
7594     level->continuous_snapping = FALSE;
7595   }
7596
7597   // only few elements were able to actively move into acid before 3.1.0
7598   // trigger settings did not exist before 3.1.0; set to default "any"
7599   if (level->game_version < VERSION_IDENT(3,1,0,0))
7600   {
7601     // correct "can move into acid" settings (all zero in old levels)
7602
7603     level->can_move_into_acid_bits = 0; // nothing can move into acid
7604     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7605
7606     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7607     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7608     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7609     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7610
7611     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7612       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7613
7614     // correct trigger settings (stored as zero == "none" in old levels)
7615
7616     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7617     {
7618       int element = EL_CUSTOM_START + i;
7619       struct ElementInfo *ei = &element_info[element];
7620
7621       for (j = 0; j < ei->num_change_pages; j++)
7622       {
7623         struct ElementChangeInfo *change = &ei->change_page[j];
7624
7625         change->trigger_player = CH_PLAYER_ANY;
7626         change->trigger_page = CH_PAGE_ANY;
7627       }
7628     }
7629   }
7630
7631   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7632   {
7633     int element = EL_CUSTOM_256;
7634     struct ElementInfo *ei = &element_info[element];
7635     struct ElementChangeInfo *change = &ei->change_page[0];
7636
7637     /* This is needed to fix a problem that was caused by a bugfix in function
7638        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7639        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7640        not replace walkable elements, but instead just placed the player on it,
7641        without placing the Sokoban field under the player). Unfortunately, this
7642        breaks "Snake Bite" style levels when the snake is halfway through a door
7643        that just closes (the snake head is still alive and can be moved in this
7644        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7645        player (without Sokoban element) which then gets killed as designed). */
7646
7647     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7648          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7649         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7650       change->target_element = EL_PLAYER_1;
7651   }
7652
7653   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7654   if (level->game_version < VERSION_IDENT(3,2,5,0))
7655   {
7656     /* This is needed to fix a problem that was caused by a bugfix in function
7657        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7658        corrects the behaviour when a custom element changes to another custom
7659        element with a higher element number that has change actions defined.
7660        Normally, only one change per frame is allowed for custom elements.
7661        Therefore, it is checked if a custom element already changed in the
7662        current frame; if it did, subsequent changes are suppressed.
7663        Unfortunately, this is only checked for element changes, but not for
7664        change actions, which are still executed. As the function above loops
7665        through all custom elements from lower to higher, an element change
7666        resulting in a lower CE number won't be checked again, while a target
7667        element with a higher number will also be checked, and potential change
7668        actions will get executed for this CE, too (which is wrong), while
7669        further changes are ignored (which is correct). As this bugfix breaks
7670        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7671        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7672        behaviour for existing levels and tapes that make use of this bug */
7673
7674     level->use_action_after_change_bug = TRUE;
7675   }
7676
7677   // not centering level after relocating player was default only in 3.2.3
7678   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7679     level->shifted_relocation = TRUE;
7680
7681   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7682   if (level->game_version < VERSION_IDENT(3,2,6,0))
7683     level->em_explodes_by_fire = TRUE;
7684
7685   // levels were solved by the first player entering an exit up to 4.1.0.0
7686   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7687     level->solved_by_one_player = TRUE;
7688
7689   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7690   if (level->game_version < VERSION_IDENT(4,1,1,1))
7691     level->use_life_bugs = TRUE;
7692
7693   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7694   if (level->game_version < VERSION_IDENT(4,1,1,1))
7695     level->sb_objects_needed = FALSE;
7696
7697   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7698   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7699     level->finish_dig_collect = FALSE;
7700
7701   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7702   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7703     level->keep_walkable_ce = TRUE;
7704 }
7705
7706 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7707 {
7708   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7709   int x, y;
7710
7711   // check if this level is (not) a Sokoban level
7712   for (y = 0; y < level->fieldy; y++)
7713     for (x = 0; x < level->fieldx; x++)
7714       if (!IS_SB_ELEMENT(Tile[x][y]))
7715         is_sokoban_level = FALSE;
7716
7717   if (is_sokoban_level)
7718   {
7719     // set special level settings for Sokoban levels
7720     SetLevelSettings_SB(level);
7721   }
7722 }
7723
7724 static void LoadLevel_InitSettings(struct LevelInfo *level)
7725 {
7726   // adjust level settings for (non-native) Sokoban-style levels
7727   LoadLevel_InitSettings_SB(level);
7728
7729   // rename levels with title "nameless level" or if renaming is forced
7730   if (leveldir_current->empty_level_name != NULL &&
7731       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7732        leveldir_current->force_level_name))
7733     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7734              leveldir_current->empty_level_name, level_nr);
7735 }
7736
7737 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7738 {
7739   int i, x, y;
7740
7741   // map elements that have changed in newer versions
7742   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7743                                                     level->game_version);
7744   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7745     for (x = 0; x < 3; x++)
7746       for (y = 0; y < 3; y++)
7747         level->yamyam_content[i].e[x][y] =
7748           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7749                                     level->game_version);
7750
7751 }
7752
7753 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7754 {
7755   int i, j;
7756
7757   // map custom element change events that have changed in newer versions
7758   // (these following values were accidentally changed in version 3.0.1)
7759   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7760   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7761   {
7762     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7763     {
7764       int element = EL_CUSTOM_START + i;
7765
7766       // order of checking and copying events to be mapped is important
7767       // (do not change the start and end value -- they are constant)
7768       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7769       {
7770         if (HAS_CHANGE_EVENT(element, j - 2))
7771         {
7772           SET_CHANGE_EVENT(element, j - 2, FALSE);
7773           SET_CHANGE_EVENT(element, j, TRUE);
7774         }
7775       }
7776
7777       // order of checking and copying events to be mapped is important
7778       // (do not change the start and end value -- they are constant)
7779       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7780       {
7781         if (HAS_CHANGE_EVENT(element, j - 1))
7782         {
7783           SET_CHANGE_EVENT(element, j - 1, FALSE);
7784           SET_CHANGE_EVENT(element, j, TRUE);
7785         }
7786       }
7787     }
7788   }
7789
7790   // initialize "can_change" field for old levels with only one change page
7791   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7792   {
7793     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7794     {
7795       int element = EL_CUSTOM_START + i;
7796
7797       if (CAN_CHANGE(element))
7798         element_info[element].change->can_change = TRUE;
7799     }
7800   }
7801
7802   // correct custom element values (for old levels without these options)
7803   if (level->game_version < VERSION_IDENT(3,1,1,0))
7804   {
7805     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7806     {
7807       int element = EL_CUSTOM_START + i;
7808       struct ElementInfo *ei = &element_info[element];
7809
7810       if (ei->access_direction == MV_NO_DIRECTION)
7811         ei->access_direction = MV_ALL_DIRECTIONS;
7812     }
7813   }
7814
7815   // correct custom element values (fix invalid values for all versions)
7816   if (1)
7817   {
7818     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7819     {
7820       int element = EL_CUSTOM_START + i;
7821       struct ElementInfo *ei = &element_info[element];
7822
7823       for (j = 0; j < ei->num_change_pages; j++)
7824       {
7825         struct ElementChangeInfo *change = &ei->change_page[j];
7826
7827         if (change->trigger_player == CH_PLAYER_NONE)
7828           change->trigger_player = CH_PLAYER_ANY;
7829
7830         if (change->trigger_side == CH_SIDE_NONE)
7831           change->trigger_side = CH_SIDE_ANY;
7832       }
7833     }
7834   }
7835
7836   // initialize "can_explode" field for old levels which did not store this
7837   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7838   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7839   {
7840     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7841     {
7842       int element = EL_CUSTOM_START + i;
7843
7844       if (EXPLODES_1X1_OLD(element))
7845         element_info[element].explosion_type = EXPLODES_1X1;
7846
7847       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7848                                              EXPLODES_SMASHED(element) ||
7849                                              EXPLODES_IMPACT(element)));
7850     }
7851   }
7852
7853   // correct previously hard-coded move delay values for maze runner style
7854   if (level->game_version < VERSION_IDENT(3,1,1,0))
7855   {
7856     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7857     {
7858       int element = EL_CUSTOM_START + i;
7859
7860       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7861       {
7862         // previously hard-coded and therefore ignored
7863         element_info[element].move_delay_fixed = 9;
7864         element_info[element].move_delay_random = 0;
7865       }
7866     }
7867   }
7868
7869   // set some other uninitialized values of custom elements in older levels
7870   if (level->game_version < VERSION_IDENT(3,1,0,0))
7871   {
7872     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7873     {
7874       int element = EL_CUSTOM_START + i;
7875
7876       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7877
7878       element_info[element].explosion_delay = 17;
7879       element_info[element].ignition_delay = 8;
7880     }
7881   }
7882
7883   // set mouse click change events to work for left/middle/right mouse button
7884   if (level->game_version < VERSION_IDENT(4,2,3,0))
7885   {
7886     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7887     {
7888       int element = EL_CUSTOM_START + i;
7889       struct ElementInfo *ei = &element_info[element];
7890
7891       for (j = 0; j < ei->num_change_pages; j++)
7892       {
7893         struct ElementChangeInfo *change = &ei->change_page[j];
7894
7895         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7896             change->has_event[CE_PRESSED_BY_MOUSE] ||
7897             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7898             change->has_event[CE_MOUSE_PRESSED_ON_X])
7899           change->trigger_side = CH_SIDE_ANY;
7900       }
7901     }
7902   }
7903 }
7904
7905 static void LoadLevel_InitElements(struct LevelInfo *level)
7906 {
7907   LoadLevel_InitStandardElements(level);
7908
7909   if (level->file_has_custom_elements)
7910     LoadLevel_InitCustomElements(level);
7911
7912   // initialize element properties for level editor etc.
7913   InitElementPropertiesEngine(level->game_version);
7914   InitElementPropertiesGfxElement();
7915 }
7916
7917 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7918 {
7919   int x, y;
7920
7921   // map elements that have changed in newer versions
7922   for (y = 0; y < level->fieldy; y++)
7923     for (x = 0; x < level->fieldx; x++)
7924       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7925                                                      level->game_version);
7926
7927   // clear unused playfield data (nicer if level gets resized in editor)
7928   for (x = 0; x < MAX_LEV_FIELDX; x++)
7929     for (y = 0; y < MAX_LEV_FIELDY; y++)
7930       if (x >= level->fieldx || y >= level->fieldy)
7931         level->field[x][y] = EL_EMPTY;
7932
7933   // copy elements to runtime playfield array
7934   for (x = 0; x < MAX_LEV_FIELDX; x++)
7935     for (y = 0; y < MAX_LEV_FIELDY; y++)
7936       Tile[x][y] = level->field[x][y];
7937
7938   // initialize level size variables for faster access
7939   lev_fieldx = level->fieldx;
7940   lev_fieldy = level->fieldy;
7941
7942   // determine border element for this level
7943   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7944     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7945   else
7946     SetBorderElement();
7947 }
7948
7949 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7950 {
7951   struct LevelFileInfo *level_file_info = &level->file_info;
7952
7953   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7954     CopyNativeLevel_RND_to_Native(level);
7955 }
7956
7957 static void LoadLevelTemplate_LoadAndInit(void)
7958 {
7959   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7960
7961   LoadLevel_InitVersion(&level_template);
7962   LoadLevel_InitElements(&level_template);
7963   LoadLevel_InitSettings(&level_template);
7964
7965   ActivateLevelTemplate();
7966 }
7967
7968 void LoadLevelTemplate(int nr)
7969 {
7970   if (!fileExists(getGlobalLevelTemplateFilename()))
7971   {
7972     Warn("no level template found for this level");
7973
7974     return;
7975   }
7976
7977   setLevelFileInfo(&level_template.file_info, nr);
7978
7979   LoadLevelTemplate_LoadAndInit();
7980 }
7981
7982 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7983 {
7984   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7985
7986   LoadLevelTemplate_LoadAndInit();
7987 }
7988
7989 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7990 {
7991   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7992
7993   if (level.use_custom_template)
7994   {
7995     if (network_level != NULL)
7996       LoadNetworkLevelTemplate(network_level);
7997     else
7998       LoadLevelTemplate(-1);
7999   }
8000
8001   LoadLevel_InitVersion(&level);
8002   LoadLevel_InitElements(&level);
8003   LoadLevel_InitPlayfield(&level);
8004   LoadLevel_InitSettings(&level);
8005
8006   LoadLevel_InitNativeEngines(&level);
8007 }
8008
8009 void LoadLevel(int nr)
8010 {
8011   SetLevelSetInfo(leveldir_current->identifier, nr);
8012
8013   setLevelFileInfo(&level.file_info, nr);
8014
8015   LoadLevel_LoadAndInit(NULL);
8016 }
8017
8018 void LoadLevelInfoOnly(int nr)
8019 {
8020   setLevelFileInfo(&level.file_info, nr);
8021
8022   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
8023 }
8024
8025 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
8026 {
8027   SetLevelSetInfo(network_level->leveldir_identifier,
8028                   network_level->file_info.nr);
8029
8030   copyLevelFileInfo(&network_level->file_info, &level.file_info);
8031
8032   LoadLevel_LoadAndInit(network_level);
8033 }
8034
8035 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
8036 {
8037   int chunk_size = 0;
8038
8039   chunk_size += putFileVersion(file, level->file_version);
8040   chunk_size += putFileVersion(file, level->game_version);
8041
8042   return chunk_size;
8043 }
8044
8045 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
8046 {
8047   int chunk_size = 0;
8048
8049   chunk_size += putFile16BitBE(file, level->creation_date.year);
8050   chunk_size += putFile8Bit(file,    level->creation_date.month);
8051   chunk_size += putFile8Bit(file,    level->creation_date.day);
8052
8053   return chunk_size;
8054 }
8055
8056 #if ENABLE_HISTORIC_CHUNKS
8057 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
8058 {
8059   int i, x, y;
8060
8061   putFile8Bit(file, level->fieldx);
8062   putFile8Bit(file, level->fieldy);
8063
8064   putFile16BitBE(file, level->time);
8065   putFile16BitBE(file, level->gems_needed);
8066
8067   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8068     putFile8Bit(file, level->name[i]);
8069
8070   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
8071     putFile8Bit(file, level->score[i]);
8072
8073   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
8074     for (y = 0; y < 3; y++)
8075       for (x = 0; x < 3; x++)
8076         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8077                            level->yamyam_content[i].e[x][y]));
8078   putFile8Bit(file, level->amoeba_speed);
8079   putFile8Bit(file, level->time_magic_wall);
8080   putFile8Bit(file, level->time_wheel);
8081   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8082                      level->amoeba_content));
8083   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8084   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8085   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8086   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8087
8088   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8089
8090   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8091   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8092   putFile32BitBE(file, level->can_move_into_acid_bits);
8093   putFile8Bit(file, level->dont_collide_with_bits);
8094
8095   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8096   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8097
8098   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8099   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8100   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8101
8102   putFile8Bit(file, level->game_engine_type);
8103
8104   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8105 }
8106 #endif
8107
8108 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8109 {
8110   int chunk_size = 0;
8111   int i;
8112
8113   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8114     chunk_size += putFile8Bit(file, level->name[i]);
8115
8116   return chunk_size;
8117 }
8118
8119 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8120 {
8121   int chunk_size = 0;
8122   int i;
8123
8124   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8125     chunk_size += putFile8Bit(file, level->author[i]);
8126
8127   return chunk_size;
8128 }
8129
8130 #if ENABLE_HISTORIC_CHUNKS
8131 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8132 {
8133   int chunk_size = 0;
8134   int x, y;
8135
8136   for (y = 0; y < level->fieldy; y++)
8137     for (x = 0; x < level->fieldx; x++)
8138       if (level->encoding_16bit_field)
8139         chunk_size += putFile16BitBE(file, level->field[x][y]);
8140       else
8141         chunk_size += putFile8Bit(file, level->field[x][y]);
8142
8143   return chunk_size;
8144 }
8145 #endif
8146
8147 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8148 {
8149   int chunk_size = 0;
8150   int x, y;
8151
8152   for (y = 0; y < level->fieldy; y++) 
8153     for (x = 0; x < level->fieldx; x++) 
8154       chunk_size += putFile16BitBE(file, level->field[x][y]);
8155
8156   return chunk_size;
8157 }
8158
8159 #if ENABLE_HISTORIC_CHUNKS
8160 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8161 {
8162   int i, x, y;
8163
8164   putFile8Bit(file, EL_YAMYAM);
8165   putFile8Bit(file, level->num_yamyam_contents);
8166   putFile8Bit(file, 0);
8167   putFile8Bit(file, 0);
8168
8169   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8170     for (y = 0; y < 3; y++)
8171       for (x = 0; x < 3; x++)
8172         if (level->encoding_16bit_field)
8173           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8174         else
8175           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8176 }
8177 #endif
8178
8179 #if ENABLE_HISTORIC_CHUNKS
8180 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8181 {
8182   int i, x, y;
8183   int num_contents, content_xsize, content_ysize;
8184   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8185
8186   if (element == EL_YAMYAM)
8187   {
8188     num_contents = level->num_yamyam_contents;
8189     content_xsize = 3;
8190     content_ysize = 3;
8191
8192     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8193       for (y = 0; y < 3; y++)
8194         for (x = 0; x < 3; x++)
8195           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8196   }
8197   else if (element == EL_BD_AMOEBA)
8198   {
8199     num_contents = 1;
8200     content_xsize = 1;
8201     content_ysize = 1;
8202
8203     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8204       for (y = 0; y < 3; y++)
8205         for (x = 0; x < 3; x++)
8206           content_array[i][x][y] = EL_EMPTY;
8207     content_array[0][0][0] = level->amoeba_content;
8208   }
8209   else
8210   {
8211     // chunk header already written -- write empty chunk data
8212     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8213
8214     Warn("cannot save content for element '%d'", element);
8215
8216     return;
8217   }
8218
8219   putFile16BitBE(file, element);
8220   putFile8Bit(file, num_contents);
8221   putFile8Bit(file, content_xsize);
8222   putFile8Bit(file, content_ysize);
8223
8224   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8225
8226   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8227     for (y = 0; y < 3; y++)
8228       for (x = 0; x < 3; x++)
8229         putFile16BitBE(file, content_array[i][x][y]);
8230 }
8231 #endif
8232
8233 #if ENABLE_HISTORIC_CHUNKS
8234 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8235 {
8236   int envelope_nr = element - EL_ENVELOPE_1;
8237   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8238   int chunk_size = 0;
8239   int i;
8240
8241   chunk_size += putFile16BitBE(file, element);
8242   chunk_size += putFile16BitBE(file, envelope_len);
8243   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8244   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8245
8246   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8247   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8248
8249   for (i = 0; i < envelope_len; i++)
8250     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8251
8252   return chunk_size;
8253 }
8254 #endif
8255
8256 #if ENABLE_HISTORIC_CHUNKS
8257 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8258                            int num_changed_custom_elements)
8259 {
8260   int i, check = 0;
8261
8262   putFile16BitBE(file, num_changed_custom_elements);
8263
8264   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8265   {
8266     int element = EL_CUSTOM_START + i;
8267
8268     struct ElementInfo *ei = &element_info[element];
8269
8270     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8271     {
8272       if (check < num_changed_custom_elements)
8273       {
8274         putFile16BitBE(file, element);
8275         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8276       }
8277
8278       check++;
8279     }
8280   }
8281
8282   if (check != num_changed_custom_elements)     // should not happen
8283     Warn("inconsistent number of custom element properties");
8284 }
8285 #endif
8286
8287 #if ENABLE_HISTORIC_CHUNKS
8288 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8289                            int num_changed_custom_elements)
8290 {
8291   int i, check = 0;
8292
8293   putFile16BitBE(file, num_changed_custom_elements);
8294
8295   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8296   {
8297     int element = EL_CUSTOM_START + i;
8298
8299     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8300     {
8301       if (check < num_changed_custom_elements)
8302       {
8303         putFile16BitBE(file, element);
8304         putFile16BitBE(file, element_info[element].change->target_element);
8305       }
8306
8307       check++;
8308     }
8309   }
8310
8311   if (check != num_changed_custom_elements)     // should not happen
8312     Warn("inconsistent number of custom target elements");
8313 }
8314 #endif
8315
8316 #if ENABLE_HISTORIC_CHUNKS
8317 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8318                            int num_changed_custom_elements)
8319 {
8320   int i, j, x, y, check = 0;
8321
8322   putFile16BitBE(file, num_changed_custom_elements);
8323
8324   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8325   {
8326     int element = EL_CUSTOM_START + i;
8327     struct ElementInfo *ei = &element_info[element];
8328
8329     if (ei->modified_settings)
8330     {
8331       if (check < num_changed_custom_elements)
8332       {
8333         putFile16BitBE(file, element);
8334
8335         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8336           putFile8Bit(file, ei->description[j]);
8337
8338         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8339
8340         // some free bytes for future properties and padding
8341         WriteUnusedBytesToFile(file, 7);
8342
8343         putFile8Bit(file, ei->use_gfx_element);
8344         putFile16BitBE(file, ei->gfx_element_initial);
8345
8346         putFile8Bit(file, ei->collect_score_initial);
8347         putFile8Bit(file, ei->collect_count_initial);
8348
8349         putFile16BitBE(file, ei->push_delay_fixed);
8350         putFile16BitBE(file, ei->push_delay_random);
8351         putFile16BitBE(file, ei->move_delay_fixed);
8352         putFile16BitBE(file, ei->move_delay_random);
8353
8354         putFile16BitBE(file, ei->move_pattern);
8355         putFile8Bit(file, ei->move_direction_initial);
8356         putFile8Bit(file, ei->move_stepsize);
8357
8358         for (y = 0; y < 3; y++)
8359           for (x = 0; x < 3; x++)
8360             putFile16BitBE(file, ei->content.e[x][y]);
8361
8362         putFile32BitBE(file, ei->change->events);
8363
8364         putFile16BitBE(file, ei->change->target_element);
8365
8366         putFile16BitBE(file, ei->change->delay_fixed);
8367         putFile16BitBE(file, ei->change->delay_random);
8368         putFile16BitBE(file, ei->change->delay_frames);
8369
8370         putFile16BitBE(file, ei->change->initial_trigger_element);
8371
8372         putFile8Bit(file, ei->change->explode);
8373         putFile8Bit(file, ei->change->use_target_content);
8374         putFile8Bit(file, ei->change->only_if_complete);
8375         putFile8Bit(file, ei->change->use_random_replace);
8376
8377         putFile8Bit(file, ei->change->random_percentage);
8378         putFile8Bit(file, ei->change->replace_when);
8379
8380         for (y = 0; y < 3; y++)
8381           for (x = 0; x < 3; x++)
8382             putFile16BitBE(file, ei->change->content.e[x][y]);
8383
8384         putFile8Bit(file, ei->slippery_type);
8385
8386         // some free bytes for future properties and padding
8387         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8388       }
8389
8390       check++;
8391     }
8392   }
8393
8394   if (check != num_changed_custom_elements)     // should not happen
8395     Warn("inconsistent number of custom element properties");
8396 }
8397 #endif
8398
8399 #if ENABLE_HISTORIC_CHUNKS
8400 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8401 {
8402   struct ElementInfo *ei = &element_info[element];
8403   int i, j, x, y;
8404
8405   // ---------- custom element base property values (96 bytes) ----------------
8406
8407   putFile16BitBE(file, element);
8408
8409   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8410     putFile8Bit(file, ei->description[i]);
8411
8412   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8413
8414   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8415
8416   putFile8Bit(file, ei->num_change_pages);
8417
8418   putFile16BitBE(file, ei->ce_value_fixed_initial);
8419   putFile16BitBE(file, ei->ce_value_random_initial);
8420   putFile8Bit(file, ei->use_last_ce_value);
8421
8422   putFile8Bit(file, ei->use_gfx_element);
8423   putFile16BitBE(file, ei->gfx_element_initial);
8424
8425   putFile8Bit(file, ei->collect_score_initial);
8426   putFile8Bit(file, ei->collect_count_initial);
8427
8428   putFile8Bit(file, ei->drop_delay_fixed);
8429   putFile8Bit(file, ei->push_delay_fixed);
8430   putFile8Bit(file, ei->drop_delay_random);
8431   putFile8Bit(file, ei->push_delay_random);
8432   putFile16BitBE(file, ei->move_delay_fixed);
8433   putFile16BitBE(file, ei->move_delay_random);
8434
8435   // bits 0 - 15 of "move_pattern" ...
8436   putFile16BitBE(file, ei->move_pattern & 0xffff);
8437   putFile8Bit(file, ei->move_direction_initial);
8438   putFile8Bit(file, ei->move_stepsize);
8439
8440   putFile8Bit(file, ei->slippery_type);
8441
8442   for (y = 0; y < 3; y++)
8443     for (x = 0; x < 3; x++)
8444       putFile16BitBE(file, ei->content.e[x][y]);
8445
8446   putFile16BitBE(file, ei->move_enter_element);
8447   putFile16BitBE(file, ei->move_leave_element);
8448   putFile8Bit(file, ei->move_leave_type);
8449
8450   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8451   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8452
8453   putFile8Bit(file, ei->access_direction);
8454
8455   putFile8Bit(file, ei->explosion_delay);
8456   putFile8Bit(file, ei->ignition_delay);
8457   putFile8Bit(file, ei->explosion_type);
8458
8459   // some free bytes for future custom property values and padding
8460   WriteUnusedBytesToFile(file, 1);
8461
8462   // ---------- change page property values (48 bytes) ------------------------
8463
8464   for (i = 0; i < ei->num_change_pages; i++)
8465   {
8466     struct ElementChangeInfo *change = &ei->change_page[i];
8467     unsigned int event_bits;
8468
8469     // bits 0 - 31 of "has_event[]" ...
8470     event_bits = 0;
8471     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8472       if (change->has_event[j])
8473         event_bits |= (1u << j);
8474     putFile32BitBE(file, event_bits);
8475
8476     putFile16BitBE(file, change->target_element);
8477
8478     putFile16BitBE(file, change->delay_fixed);
8479     putFile16BitBE(file, change->delay_random);
8480     putFile16BitBE(file, change->delay_frames);
8481
8482     putFile16BitBE(file, change->initial_trigger_element);
8483
8484     putFile8Bit(file, change->explode);
8485     putFile8Bit(file, change->use_target_content);
8486     putFile8Bit(file, change->only_if_complete);
8487     putFile8Bit(file, change->use_random_replace);
8488
8489     putFile8Bit(file, change->random_percentage);
8490     putFile8Bit(file, change->replace_when);
8491
8492     for (y = 0; y < 3; y++)
8493       for (x = 0; x < 3; x++)
8494         putFile16BitBE(file, change->target_content.e[x][y]);
8495
8496     putFile8Bit(file, change->can_change);
8497
8498     putFile8Bit(file, change->trigger_side);
8499
8500     putFile8Bit(file, change->trigger_player);
8501     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8502                        log_2(change->trigger_page)));
8503
8504     putFile8Bit(file, change->has_action);
8505     putFile8Bit(file, change->action_type);
8506     putFile8Bit(file, change->action_mode);
8507     putFile16BitBE(file, change->action_arg);
8508
8509     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8510     event_bits = 0;
8511     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8512       if (change->has_event[j])
8513         event_bits |= (1u << (j - 32));
8514     putFile8Bit(file, event_bits);
8515   }
8516 }
8517 #endif
8518
8519 #if ENABLE_HISTORIC_CHUNKS
8520 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8521 {
8522   struct ElementInfo *ei = &element_info[element];
8523   struct ElementGroupInfo *group = ei->group;
8524   int i;
8525
8526   putFile16BitBE(file, element);
8527
8528   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8529     putFile8Bit(file, ei->description[i]);
8530
8531   putFile8Bit(file, group->num_elements);
8532
8533   putFile8Bit(file, ei->use_gfx_element);
8534   putFile16BitBE(file, ei->gfx_element_initial);
8535
8536   putFile8Bit(file, group->choice_mode);
8537
8538   // some free bytes for future values and padding
8539   WriteUnusedBytesToFile(file, 3);
8540
8541   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8542     putFile16BitBE(file, group->element[i]);
8543 }
8544 #endif
8545
8546 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8547                                 boolean write_element)
8548 {
8549   int save_type = entry->save_type;
8550   int data_type = entry->data_type;
8551   int conf_type = entry->conf_type;
8552   int byte_mask = conf_type & CONF_MASK_BYTES;
8553   int element = entry->element;
8554   int default_value = entry->default_value;
8555   int num_bytes = 0;
8556   boolean modified = FALSE;
8557
8558   if (byte_mask != CONF_MASK_MULTI_BYTES)
8559   {
8560     void *value_ptr = entry->value;
8561     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8562                  *(int *)value_ptr);
8563
8564     // check if any settings have been modified before saving them
8565     if (value != default_value)
8566       modified = TRUE;
8567
8568     // do not save if explicitly told or if unmodified default settings
8569     if ((save_type == SAVE_CONF_NEVER) ||
8570         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8571       return 0;
8572
8573     if (write_element)
8574       num_bytes += putFile16BitBE(file, element);
8575
8576     num_bytes += putFile8Bit(file, conf_type);
8577     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8578                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8579                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8580                   0);
8581   }
8582   else if (data_type == TYPE_STRING)
8583   {
8584     char *default_string = entry->default_string;
8585     char *string = (char *)(entry->value);
8586     int string_length = strlen(string);
8587     int i;
8588
8589     // check if any settings have been modified before saving them
8590     if (!strEqual(string, default_string))
8591       modified = TRUE;
8592
8593     // do not save if explicitly told or if unmodified default settings
8594     if ((save_type == SAVE_CONF_NEVER) ||
8595         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8596       return 0;
8597
8598     if (write_element)
8599       num_bytes += putFile16BitBE(file, element);
8600
8601     num_bytes += putFile8Bit(file, conf_type);
8602     num_bytes += putFile16BitBE(file, string_length);
8603
8604     for (i = 0; i < string_length; i++)
8605       num_bytes += putFile8Bit(file, string[i]);
8606   }
8607   else if (data_type == TYPE_ELEMENT_LIST)
8608   {
8609     int *element_array = (int *)(entry->value);
8610     int num_elements = *(int *)(entry->num_entities);
8611     int i;
8612
8613     // check if any settings have been modified before saving them
8614     for (i = 0; i < num_elements; i++)
8615       if (element_array[i] != default_value)
8616         modified = TRUE;
8617
8618     // do not save if explicitly told or if unmodified default settings
8619     if ((save_type == SAVE_CONF_NEVER) ||
8620         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8621       return 0;
8622
8623     if (write_element)
8624       num_bytes += putFile16BitBE(file, element);
8625
8626     num_bytes += putFile8Bit(file, conf_type);
8627     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8628
8629     for (i = 0; i < num_elements; i++)
8630       num_bytes += putFile16BitBE(file, element_array[i]);
8631   }
8632   else if (data_type == TYPE_CONTENT_LIST)
8633   {
8634     struct Content *content = (struct Content *)(entry->value);
8635     int num_contents = *(int *)(entry->num_entities);
8636     int i, x, y;
8637
8638     // check if any settings have been modified before saving them
8639     for (i = 0; i < num_contents; i++)
8640       for (y = 0; y < 3; y++)
8641         for (x = 0; x < 3; x++)
8642           if (content[i].e[x][y] != default_value)
8643             modified = TRUE;
8644
8645     // do not save if explicitly told or if unmodified default settings
8646     if ((save_type == SAVE_CONF_NEVER) ||
8647         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8648       return 0;
8649
8650     if (write_element)
8651       num_bytes += putFile16BitBE(file, element);
8652
8653     num_bytes += putFile8Bit(file, conf_type);
8654     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8655
8656     for (i = 0; i < num_contents; i++)
8657       for (y = 0; y < 3; y++)
8658         for (x = 0; x < 3; x++)
8659           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8660   }
8661
8662   return num_bytes;
8663 }
8664
8665 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8666 {
8667   int chunk_size = 0;
8668   int i;
8669
8670   li = *level;          // copy level data into temporary buffer
8671
8672   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8673     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8674
8675   return chunk_size;
8676 }
8677
8678 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8679 {
8680   int chunk_size = 0;
8681   int i;
8682
8683   li = *level;          // copy level data into temporary buffer
8684
8685   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8686     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8687
8688   return chunk_size;
8689 }
8690
8691 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8692 {
8693   int envelope_nr = element - EL_ENVELOPE_1;
8694   int chunk_size = 0;
8695   int i;
8696
8697   chunk_size += putFile16BitBE(file, element);
8698
8699   // copy envelope data into temporary buffer
8700   xx_envelope = level->envelope[envelope_nr];
8701
8702   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8703     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8704
8705   return chunk_size;
8706 }
8707
8708 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8709 {
8710   struct ElementInfo *ei = &element_info[element];
8711   int chunk_size = 0;
8712   int i, j;
8713
8714   chunk_size += putFile16BitBE(file, element);
8715
8716   xx_ei = *ei;          // copy element data into temporary buffer
8717
8718   // set default description string for this specific element
8719   strcpy(xx_default_description, getDefaultElementDescription(ei));
8720
8721   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8722     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8723
8724   for (i = 0; i < ei->num_change_pages; i++)
8725   {
8726     struct ElementChangeInfo *change = &ei->change_page[i];
8727
8728     xx_current_change_page = i;
8729
8730     xx_change = *change;        // copy change data into temporary buffer
8731
8732     resetEventBits();
8733     setEventBitsFromEventFlags(change);
8734
8735     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8736       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8737                                          FALSE);
8738   }
8739
8740   return chunk_size;
8741 }
8742
8743 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8744 {
8745   struct ElementInfo *ei = &element_info[element];
8746   struct ElementGroupInfo *group = ei->group;
8747   int chunk_size = 0;
8748   int i;
8749
8750   chunk_size += putFile16BitBE(file, element);
8751
8752   xx_ei = *ei;          // copy element data into temporary buffer
8753   xx_group = *group;    // copy group data into temporary buffer
8754
8755   // set default description string for this specific element
8756   strcpy(xx_default_description, getDefaultElementDescription(ei));
8757
8758   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8759     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8760
8761   return chunk_size;
8762 }
8763
8764 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8765 {
8766   struct ElementInfo *ei = &element_info[element];
8767   int chunk_size = 0;
8768   int i;
8769
8770   chunk_size += putFile16BitBE(file, element);
8771
8772   xx_ei = *ei;          // copy element data into temporary buffer
8773
8774   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8775     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8776
8777   return chunk_size;
8778 }
8779
8780 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8781                                   boolean save_as_template)
8782 {
8783   int chunk_size;
8784   int i;
8785   FILE *file;
8786
8787   if (!(file = fopen(filename, MODE_WRITE)))
8788   {
8789     Warn("cannot save level file '%s'", filename);
8790
8791     return;
8792   }
8793
8794   level->file_version = FILE_VERSION_ACTUAL;
8795   level->game_version = GAME_VERSION_ACTUAL;
8796
8797   level->creation_date = getCurrentDate();
8798
8799   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8800   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8801
8802   chunk_size = SaveLevel_VERS(NULL, level);
8803   putFileChunkBE(file, "VERS", chunk_size);
8804   SaveLevel_VERS(file, level);
8805
8806   chunk_size = SaveLevel_DATE(NULL, level);
8807   putFileChunkBE(file, "DATE", chunk_size);
8808   SaveLevel_DATE(file, level);
8809
8810   chunk_size = SaveLevel_NAME(NULL, level);
8811   putFileChunkBE(file, "NAME", chunk_size);
8812   SaveLevel_NAME(file, level);
8813
8814   chunk_size = SaveLevel_AUTH(NULL, level);
8815   putFileChunkBE(file, "AUTH", chunk_size);
8816   SaveLevel_AUTH(file, level);
8817
8818   chunk_size = SaveLevel_INFO(NULL, level);
8819   putFileChunkBE(file, "INFO", chunk_size);
8820   SaveLevel_INFO(file, level);
8821
8822   chunk_size = SaveLevel_BODY(NULL, level);
8823   putFileChunkBE(file, "BODY", chunk_size);
8824   SaveLevel_BODY(file, level);
8825
8826   chunk_size = SaveLevel_ELEM(NULL, level);
8827   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8828   {
8829     putFileChunkBE(file, "ELEM", chunk_size);
8830     SaveLevel_ELEM(file, level);
8831   }
8832
8833   for (i = 0; i < NUM_ENVELOPES; i++)
8834   {
8835     int element = EL_ENVELOPE_1 + i;
8836
8837     chunk_size = SaveLevel_NOTE(NULL, level, element);
8838     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8839     {
8840       putFileChunkBE(file, "NOTE", chunk_size);
8841       SaveLevel_NOTE(file, level, element);
8842     }
8843   }
8844
8845   // if not using template level, check for non-default custom/group elements
8846   if (!level->use_custom_template || save_as_template)
8847   {
8848     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8849     {
8850       int element = EL_CUSTOM_START + i;
8851
8852       chunk_size = SaveLevel_CUSX(NULL, level, element);
8853       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8854       {
8855         putFileChunkBE(file, "CUSX", chunk_size);
8856         SaveLevel_CUSX(file, level, element);
8857       }
8858     }
8859
8860     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8861     {
8862       int element = EL_GROUP_START + i;
8863
8864       chunk_size = SaveLevel_GRPX(NULL, level, element);
8865       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8866       {
8867         putFileChunkBE(file, "GRPX", chunk_size);
8868         SaveLevel_GRPX(file, level, element);
8869       }
8870     }
8871
8872     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8873     {
8874       int element = GET_EMPTY_ELEMENT(i);
8875
8876       chunk_size = SaveLevel_EMPX(NULL, level, element);
8877       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8878       {
8879         putFileChunkBE(file, "EMPX", chunk_size);
8880         SaveLevel_EMPX(file, level, element);
8881       }
8882     }
8883   }
8884
8885   fclose(file);
8886
8887   SetFilePermissions(filename, PERMS_PRIVATE);
8888 }
8889
8890 void SaveLevel(int nr)
8891 {
8892   char *filename = getDefaultLevelFilename(nr);
8893
8894   SaveLevelFromFilename(&level, filename, FALSE);
8895 }
8896
8897 void SaveLevelTemplate(void)
8898 {
8899   char *filename = getLocalLevelTemplateFilename();
8900
8901   SaveLevelFromFilename(&level, filename, TRUE);
8902 }
8903
8904 boolean SaveLevelChecked(int nr)
8905 {
8906   char *filename = getDefaultLevelFilename(nr);
8907   boolean new_level = !fileExists(filename);
8908   boolean level_saved = FALSE;
8909
8910   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8911   {
8912     SaveLevel(nr);
8913
8914     if (new_level)
8915       Request("Level saved!", REQ_CONFIRM);
8916
8917     level_saved = TRUE;
8918   }
8919
8920   return level_saved;
8921 }
8922
8923 void DumpLevel(struct LevelInfo *level)
8924 {
8925   if (level->no_level_file || level->no_valid_file)
8926   {
8927     Warn("cannot dump -- no valid level file found");
8928
8929     return;
8930   }
8931
8932   PrintLine("-", 79);
8933   Print("Level xxx (file version %08d, game version %08d)\n",
8934         level->file_version, level->game_version);
8935   PrintLine("-", 79);
8936
8937   Print("Level author: '%s'\n", level->author);
8938   Print("Level title:  '%s'\n", level->name);
8939   Print("\n");
8940   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8941   Print("\n");
8942   Print("Level time:  %d seconds\n", level->time);
8943   Print("Gems needed: %d\n", level->gems_needed);
8944   Print("\n");
8945   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8946   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8947   Print("Time for light:      %d seconds\n", level->time_light);
8948   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8949   Print("\n");
8950   Print("Amoeba speed: %d\n", level->amoeba_speed);
8951   Print("\n");
8952
8953   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8954   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8955   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8956   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8957   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8958   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8959
8960   if (options.debug)
8961   {
8962     int i, j;
8963
8964     for (i = 0; i < NUM_ENVELOPES; i++)
8965     {
8966       char *text = level->envelope[i].text;
8967       int text_len = strlen(text);
8968       boolean has_text = FALSE;
8969
8970       for (j = 0; j < text_len; j++)
8971         if (text[j] != ' ' && text[j] != '\n')
8972           has_text = TRUE;
8973
8974       if (has_text)
8975       {
8976         Print("\n");
8977         Print("Envelope %d:\n'%s'\n", i + 1, text);
8978       }
8979     }
8980   }
8981
8982   PrintLine("-", 79);
8983 }
8984
8985 void DumpLevels(void)
8986 {
8987   static LevelDirTree *dumplevel_leveldir = NULL;
8988
8989   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8990                                                  global.dumplevel_leveldir);
8991
8992   if (dumplevel_leveldir == NULL)
8993     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8994
8995   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8996       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8997     Fail("no such level number: %d", global.dumplevel_level_nr);
8998
8999   leveldir_current = dumplevel_leveldir;
9000
9001   LoadLevel(global.dumplevel_level_nr);
9002   DumpLevel(&level);
9003
9004   CloseAllAndExit(0);
9005 }
9006
9007
9008 // ============================================================================
9009 // tape file functions
9010 // ============================================================================
9011
9012 static void setTapeInfoToDefaults(void)
9013 {
9014   int i;
9015
9016   // always start with reliable default values (empty tape)
9017   TapeErase();
9018
9019   // default values (also for pre-1.2 tapes) with only the first player
9020   tape.player_participates[0] = TRUE;
9021   for (i = 1; i < MAX_PLAYERS; i++)
9022     tape.player_participates[i] = FALSE;
9023
9024   // at least one (default: the first) player participates in every tape
9025   tape.num_participating_players = 1;
9026
9027   tape.property_bits = TAPE_PROPERTY_NONE;
9028
9029   tape.level_nr = level_nr;
9030   tape.counter = 0;
9031   tape.changed = FALSE;
9032   tape.solved = FALSE;
9033
9034   tape.recording = FALSE;
9035   tape.playing = FALSE;
9036   tape.pausing = FALSE;
9037
9038   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
9039   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
9040
9041   tape.no_info_chunk = TRUE;
9042   tape.no_valid_file = FALSE;
9043 }
9044
9045 static int getTapePosSize(struct TapeInfo *tape)
9046 {
9047   int tape_pos_size = 0;
9048
9049   if (tape->use_key_actions)
9050     tape_pos_size += tape->num_participating_players;
9051
9052   if (tape->use_mouse_actions)
9053     tape_pos_size += 3;         // x and y position and mouse button mask
9054
9055   tape_pos_size += 1;           // tape action delay value
9056
9057   return tape_pos_size;
9058 }
9059
9060 static void setTapeActionFlags(struct TapeInfo *tape, int value)
9061 {
9062   tape->use_key_actions = FALSE;
9063   tape->use_mouse_actions = FALSE;
9064
9065   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
9066     tape->use_key_actions = TRUE;
9067
9068   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
9069     tape->use_mouse_actions = TRUE;
9070 }
9071
9072 static int getTapeActionValue(struct TapeInfo *tape)
9073 {
9074   return (tape->use_key_actions &&
9075           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
9076           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9077           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9078           TAPE_ACTIONS_DEFAULT);
9079 }
9080
9081 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9082 {
9083   tape->file_version = getFileVersion(file);
9084   tape->game_version = getFileVersion(file);
9085
9086   return chunk_size;
9087 }
9088
9089 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9090 {
9091   int i;
9092
9093   tape->random_seed = getFile32BitBE(file);
9094   tape->date        = getFile32BitBE(file);
9095   tape->length      = getFile32BitBE(file);
9096
9097   // read header fields that are new since version 1.2
9098   if (tape->file_version >= FILE_VERSION_1_2)
9099   {
9100     byte store_participating_players = getFile8Bit(file);
9101     int engine_version;
9102
9103     // since version 1.2, tapes store which players participate in the tape
9104     tape->num_participating_players = 0;
9105     for (i = 0; i < MAX_PLAYERS; i++)
9106     {
9107       tape->player_participates[i] = FALSE;
9108
9109       if (store_participating_players & (1 << i))
9110       {
9111         tape->player_participates[i] = TRUE;
9112         tape->num_participating_players++;
9113       }
9114     }
9115
9116     setTapeActionFlags(tape, getFile8Bit(file));
9117
9118     tape->property_bits = getFile8Bit(file);
9119     tape->solved = getFile8Bit(file);
9120
9121     engine_version = getFileVersion(file);
9122     if (engine_version > 0)
9123       tape->engine_version = engine_version;
9124     else
9125       tape->engine_version = tape->game_version;
9126   }
9127
9128   return chunk_size;
9129 }
9130
9131 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9132 {
9133   tape->scr_fieldx = getFile8Bit(file);
9134   tape->scr_fieldy = getFile8Bit(file);
9135
9136   return chunk_size;
9137 }
9138
9139 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9140 {
9141   char *level_identifier = NULL;
9142   int level_identifier_size;
9143   int i;
9144
9145   tape->no_info_chunk = FALSE;
9146
9147   level_identifier_size = getFile16BitBE(file);
9148
9149   level_identifier = checked_malloc(level_identifier_size);
9150
9151   for (i = 0; i < level_identifier_size; i++)
9152     level_identifier[i] = getFile8Bit(file);
9153
9154   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9155   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9156
9157   checked_free(level_identifier);
9158
9159   tape->level_nr = getFile16BitBE(file);
9160
9161   chunk_size = 2 + level_identifier_size + 2;
9162
9163   return chunk_size;
9164 }
9165
9166 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9167 {
9168   int i, j;
9169   int tape_pos_size = getTapePosSize(tape);
9170   int chunk_size_expected = tape_pos_size * tape->length;
9171
9172   if (chunk_size_expected != chunk_size)
9173   {
9174     ReadUnusedBytesFromFile(file, chunk_size);
9175     return chunk_size_expected;
9176   }
9177
9178   for (i = 0; i < tape->length; i++)
9179   {
9180     if (i >= MAX_TAPE_LEN)
9181     {
9182       Warn("tape truncated -- size exceeds maximum tape size %d",
9183             MAX_TAPE_LEN);
9184
9185       // tape too large; read and ignore remaining tape data from this chunk
9186       for (;i < tape->length; i++)
9187         ReadUnusedBytesFromFile(file, tape_pos_size);
9188
9189       break;
9190     }
9191
9192     if (tape->use_key_actions)
9193     {
9194       for (j = 0; j < MAX_PLAYERS; j++)
9195       {
9196         tape->pos[i].action[j] = MV_NONE;
9197
9198         if (tape->player_participates[j])
9199           tape->pos[i].action[j] = getFile8Bit(file);
9200       }
9201     }
9202
9203     if (tape->use_mouse_actions)
9204     {
9205       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9206       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9207       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9208     }
9209
9210     tape->pos[i].delay = getFile8Bit(file);
9211
9212     if (tape->file_version == FILE_VERSION_1_0)
9213     {
9214       // eliminate possible diagonal moves in old tapes
9215       // this is only for backward compatibility
9216
9217       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9218       byte action = tape->pos[i].action[0];
9219       int k, num_moves = 0;
9220
9221       for (k = 0; k < 4; k++)
9222       {
9223         if (action & joy_dir[k])
9224         {
9225           tape->pos[i + num_moves].action[0] = joy_dir[k];
9226           if (num_moves > 0)
9227             tape->pos[i + num_moves].delay = 0;
9228           num_moves++;
9229         }
9230       }
9231
9232       if (num_moves > 1)
9233       {
9234         num_moves--;
9235         i += num_moves;
9236         tape->length += num_moves;
9237       }
9238     }
9239     else if (tape->file_version < FILE_VERSION_2_0)
9240     {
9241       // convert pre-2.0 tapes to new tape format
9242
9243       if (tape->pos[i].delay > 1)
9244       {
9245         // action part
9246         tape->pos[i + 1] = tape->pos[i];
9247         tape->pos[i + 1].delay = 1;
9248
9249         // delay part
9250         for (j = 0; j < MAX_PLAYERS; j++)
9251           tape->pos[i].action[j] = MV_NONE;
9252         tape->pos[i].delay--;
9253
9254         i++;
9255         tape->length++;
9256       }
9257     }
9258
9259     if (checkEndOfFile(file))
9260       break;
9261   }
9262
9263   if (i != tape->length)
9264     chunk_size = tape_pos_size * i;
9265
9266   return chunk_size;
9267 }
9268
9269 static void LoadTape_SokobanSolution(char *filename)
9270 {
9271   File *file;
9272   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9273
9274   if (!(file = openFile(filename, MODE_READ)))
9275   {
9276     tape.no_valid_file = TRUE;
9277
9278     return;
9279   }
9280
9281   while (!checkEndOfFile(file))
9282   {
9283     unsigned char c = getByteFromFile(file);
9284
9285     if (checkEndOfFile(file))
9286       break;
9287
9288     switch (c)
9289     {
9290       case 'u':
9291       case 'U':
9292         tape.pos[tape.length].action[0] = MV_UP;
9293         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9294         tape.length++;
9295         break;
9296
9297       case 'd':
9298       case 'D':
9299         tape.pos[tape.length].action[0] = MV_DOWN;
9300         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9301         tape.length++;
9302         break;
9303
9304       case 'l':
9305       case 'L':
9306         tape.pos[tape.length].action[0] = MV_LEFT;
9307         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9308         tape.length++;
9309         break;
9310
9311       case 'r':
9312       case 'R':
9313         tape.pos[tape.length].action[0] = MV_RIGHT;
9314         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9315         tape.length++;
9316         break;
9317
9318       case '\n':
9319       case '\r':
9320       case '\t':
9321       case ' ':
9322         // ignore white-space characters
9323         break;
9324
9325       default:
9326         tape.no_valid_file = TRUE;
9327
9328         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9329
9330         break;
9331     }
9332   }
9333
9334   closeFile(file);
9335
9336   if (tape.no_valid_file)
9337     return;
9338
9339   tape.length_frames  = GetTapeLengthFrames();
9340   tape.length_seconds = GetTapeLengthSeconds();
9341 }
9342
9343 void LoadTapeFromFilename(char *filename)
9344 {
9345   char cookie[MAX_LINE_LEN];
9346   char chunk_name[CHUNK_ID_LEN + 1];
9347   File *file;
9348   int chunk_size;
9349
9350   // always start with reliable default values
9351   setTapeInfoToDefaults();
9352
9353   if (strSuffix(filename, ".sln"))
9354   {
9355     LoadTape_SokobanSolution(filename);
9356
9357     return;
9358   }
9359
9360   if (!(file = openFile(filename, MODE_READ)))
9361   {
9362     tape.no_valid_file = TRUE;
9363
9364     return;
9365   }
9366
9367   getFileChunkBE(file, chunk_name, NULL);
9368   if (strEqual(chunk_name, "RND1"))
9369   {
9370     getFile32BitBE(file);               // not used
9371
9372     getFileChunkBE(file, chunk_name, NULL);
9373     if (!strEqual(chunk_name, "TAPE"))
9374     {
9375       tape.no_valid_file = TRUE;
9376
9377       Warn("unknown format of tape file '%s'", filename);
9378
9379       closeFile(file);
9380
9381       return;
9382     }
9383   }
9384   else  // check for pre-2.0 file format with cookie string
9385   {
9386     strcpy(cookie, chunk_name);
9387     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9388       cookie[4] = '\0';
9389     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9390       cookie[strlen(cookie) - 1] = '\0';
9391
9392     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9393     {
9394       tape.no_valid_file = TRUE;
9395
9396       Warn("unknown format of tape file '%s'", filename);
9397
9398       closeFile(file);
9399
9400       return;
9401     }
9402
9403     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9404     {
9405       tape.no_valid_file = TRUE;
9406
9407       Warn("unsupported version of tape file '%s'", filename);
9408
9409       closeFile(file);
9410
9411       return;
9412     }
9413
9414     // pre-2.0 tape files have no game version, so use file version here
9415     tape.game_version = tape.file_version;
9416   }
9417
9418   if (tape.file_version < FILE_VERSION_1_2)
9419   {
9420     // tape files from versions before 1.2.0 without chunk structure
9421     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9422     LoadTape_BODY(file, 2 * tape.length,      &tape);
9423   }
9424   else
9425   {
9426     static struct
9427     {
9428       char *name;
9429       int size;
9430       int (*loader)(File *, int, struct TapeInfo *);
9431     }
9432     chunk_info[] =
9433     {
9434       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9435       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9436       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9437       { "INFO", -1,                     LoadTape_INFO },
9438       { "BODY", -1,                     LoadTape_BODY },
9439       {  NULL,  0,                      NULL }
9440     };
9441
9442     while (getFileChunkBE(file, chunk_name, &chunk_size))
9443     {
9444       int i = 0;
9445
9446       while (chunk_info[i].name != NULL &&
9447              !strEqual(chunk_name, chunk_info[i].name))
9448         i++;
9449
9450       if (chunk_info[i].name == NULL)
9451       {
9452         Warn("unknown chunk '%s' in tape file '%s'",
9453               chunk_name, filename);
9454
9455         ReadUnusedBytesFromFile(file, chunk_size);
9456       }
9457       else if (chunk_info[i].size != -1 &&
9458                chunk_info[i].size != chunk_size)
9459       {
9460         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9461               chunk_size, chunk_name, filename);
9462
9463         ReadUnusedBytesFromFile(file, chunk_size);
9464       }
9465       else
9466       {
9467         // call function to load this tape chunk
9468         int chunk_size_expected =
9469           (chunk_info[i].loader)(file, chunk_size, &tape);
9470
9471         // the size of some chunks cannot be checked before reading other
9472         // chunks first (like "HEAD" and "BODY") that contain some header
9473         // information, so check them here
9474         if (chunk_size_expected != chunk_size)
9475         {
9476           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9477                 chunk_size, chunk_name, filename);
9478         }
9479       }
9480     }
9481   }
9482
9483   closeFile(file);
9484
9485   tape.length_frames  = GetTapeLengthFrames();
9486   tape.length_seconds = GetTapeLengthSeconds();
9487
9488 #if 0
9489   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9490         tape.file_version);
9491   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9492         tape.game_version);
9493   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9494         tape.engine_version);
9495 #endif
9496 }
9497
9498 void LoadTape(int nr)
9499 {
9500   char *filename = getTapeFilename(nr);
9501
9502   LoadTapeFromFilename(filename);
9503 }
9504
9505 void LoadSolutionTape(int nr)
9506 {
9507   char *filename = getSolutionTapeFilename(nr);
9508
9509   LoadTapeFromFilename(filename);
9510
9511   if (TAPE_IS_EMPTY(tape))
9512   {
9513     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9514         level.native_bd_level->replay != NULL)
9515       CopyNativeTape_BD_to_RND(&level);
9516     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9517         level.native_sp_level->demo.is_available)
9518       CopyNativeTape_SP_to_RND(&level);
9519   }
9520 }
9521
9522 void LoadScoreTape(char *score_tape_basename, int nr)
9523 {
9524   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9525
9526   LoadTapeFromFilename(filename);
9527 }
9528
9529 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9530 {
9531   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9532
9533   LoadTapeFromFilename(filename);
9534 }
9535
9536 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9537 {
9538   // chunk required for team mode tapes with non-default screen size
9539   return (tape->num_participating_players > 1 &&
9540           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9541            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9542 }
9543
9544 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9545 {
9546   putFileVersion(file, tape->file_version);
9547   putFileVersion(file, tape->game_version);
9548 }
9549
9550 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9551 {
9552   int i;
9553   byte store_participating_players = 0;
9554
9555   // set bits for participating players for compact storage
9556   for (i = 0; i < MAX_PLAYERS; i++)
9557     if (tape->player_participates[i])
9558       store_participating_players |= (1 << i);
9559
9560   putFile32BitBE(file, tape->random_seed);
9561   putFile32BitBE(file, tape->date);
9562   putFile32BitBE(file, tape->length);
9563
9564   putFile8Bit(file, store_participating_players);
9565
9566   putFile8Bit(file, getTapeActionValue(tape));
9567
9568   putFile8Bit(file, tape->property_bits);
9569   putFile8Bit(file, tape->solved);
9570
9571   putFileVersion(file, tape->engine_version);
9572 }
9573
9574 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9575 {
9576   putFile8Bit(file, tape->scr_fieldx);
9577   putFile8Bit(file, tape->scr_fieldy);
9578 }
9579
9580 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9581 {
9582   int level_identifier_size = strlen(tape->level_identifier) + 1;
9583   int i;
9584
9585   putFile16BitBE(file, level_identifier_size);
9586
9587   for (i = 0; i < level_identifier_size; i++)
9588     putFile8Bit(file, tape->level_identifier[i]);
9589
9590   putFile16BitBE(file, tape->level_nr);
9591 }
9592
9593 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9594 {
9595   int i, j;
9596
9597   for (i = 0; i < tape->length; i++)
9598   {
9599     if (tape->use_key_actions)
9600     {
9601       for (j = 0; j < MAX_PLAYERS; j++)
9602         if (tape->player_participates[j])
9603           putFile8Bit(file, tape->pos[i].action[j]);
9604     }
9605
9606     if (tape->use_mouse_actions)
9607     {
9608       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9609       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9610       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9611     }
9612
9613     putFile8Bit(file, tape->pos[i].delay);
9614   }
9615 }
9616
9617 void SaveTapeToFilename(char *filename)
9618 {
9619   FILE *file;
9620   int tape_pos_size;
9621   int info_chunk_size;
9622   int body_chunk_size;
9623
9624   if (!(file = fopen(filename, MODE_WRITE)))
9625   {
9626     Warn("cannot save level recording file '%s'", filename);
9627
9628     return;
9629   }
9630
9631   tape_pos_size = getTapePosSize(&tape);
9632
9633   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9634   body_chunk_size = tape_pos_size * tape.length;
9635
9636   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9637   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9638
9639   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9640   SaveTape_VERS(file, &tape);
9641
9642   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9643   SaveTape_HEAD(file, &tape);
9644
9645   if (checkSaveTape_SCRN(&tape))
9646   {
9647     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9648     SaveTape_SCRN(file, &tape);
9649   }
9650
9651   putFileChunkBE(file, "INFO", info_chunk_size);
9652   SaveTape_INFO(file, &tape);
9653
9654   putFileChunkBE(file, "BODY", body_chunk_size);
9655   SaveTape_BODY(file, &tape);
9656
9657   fclose(file);
9658
9659   SetFilePermissions(filename, PERMS_PRIVATE);
9660 }
9661
9662 static void SaveTapeExt(char *filename)
9663 {
9664   int i;
9665
9666   tape.file_version = FILE_VERSION_ACTUAL;
9667   tape.game_version = GAME_VERSION_ACTUAL;
9668
9669   tape.num_participating_players = 0;
9670
9671   // count number of participating players
9672   for (i = 0; i < MAX_PLAYERS; i++)
9673     if (tape.player_participates[i])
9674       tape.num_participating_players++;
9675
9676   SaveTapeToFilename(filename);
9677
9678   tape.changed = FALSE;
9679 }
9680
9681 void SaveTape(int nr)
9682 {
9683   char *filename = getTapeFilename(nr);
9684
9685   InitTapeDirectory(leveldir_current->subdir);
9686
9687   SaveTapeExt(filename);
9688 }
9689
9690 void SaveScoreTape(int nr)
9691 {
9692   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9693
9694   // used instead of "leveldir_current->subdir" (for network games)
9695   InitScoreTapeDirectory(levelset.identifier, nr);
9696
9697   SaveTapeExt(filename);
9698 }
9699
9700 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9701                                   unsigned int req_state_added)
9702 {
9703   char *filename = getTapeFilename(nr);
9704   boolean new_tape = !fileExists(filename);
9705   boolean tape_saved = FALSE;
9706
9707   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9708   {
9709     SaveTape(nr);
9710
9711     if (new_tape)
9712       Request(msg_saved, REQ_CONFIRM | req_state_added);
9713
9714     tape_saved = TRUE;
9715   }
9716
9717   return tape_saved;
9718 }
9719
9720 boolean SaveTapeChecked(int nr)
9721 {
9722   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9723 }
9724
9725 boolean SaveTapeChecked_LevelSolved(int nr)
9726 {
9727   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9728                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9729 }
9730
9731 void DumpTape(struct TapeInfo *tape)
9732 {
9733   int tape_frame_counter;
9734   int i, j;
9735
9736   if (tape->no_valid_file)
9737   {
9738     Warn("cannot dump -- no valid tape file found");
9739
9740     return;
9741   }
9742
9743   PrintLine("-", 79);
9744
9745   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9746         tape->level_nr, tape->file_version, tape->game_version);
9747   Print("                  (effective engine version %08d)\n",
9748         tape->engine_version);
9749   Print("Level series identifier: '%s'\n", tape->level_identifier);
9750
9751   Print("Solution tape: %s\n",
9752         tape->solved ? "yes" :
9753         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9754
9755   Print("Special tape properties: ");
9756   if (tape->property_bits == TAPE_PROPERTY_NONE)
9757     Print("[none]");
9758   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9759     Print("[em_random_bug]");
9760   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9761     Print("[game_speed]");
9762   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9763     Print("[pause]");
9764   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9765     Print("[single_step]");
9766   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9767     Print("[snapshot]");
9768   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9769     Print("[replayed]");
9770   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9771     Print("[tas_keys]");
9772   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9773     Print("[small_graphics]");
9774   Print("\n");
9775
9776   int year2 = tape->date / 10000;
9777   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9778   int month_index_raw = (tape->date / 100) % 100;
9779   int month_index = month_index_raw % 12;       // prevent invalid index
9780   int month = month_index + 1;
9781   int day = tape->date % 100;
9782
9783   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9784
9785   PrintLine("-", 79);
9786
9787   tape_frame_counter = 0;
9788
9789   for (i = 0; i < tape->length; i++)
9790   {
9791     if (i >= MAX_TAPE_LEN)
9792       break;
9793
9794     Print("%04d: ", i);
9795
9796     for (j = 0; j < MAX_PLAYERS; j++)
9797     {
9798       if (tape->player_participates[j])
9799       {
9800         int action = tape->pos[i].action[j];
9801
9802         Print("%d:%02x ", j, action);
9803         Print("[%c%c%c%c|%c%c] - ",
9804               (action & JOY_LEFT ? '<' : ' '),
9805               (action & JOY_RIGHT ? '>' : ' '),
9806               (action & JOY_UP ? '^' : ' '),
9807               (action & JOY_DOWN ? 'v' : ' '),
9808               (action & JOY_BUTTON_1 ? '1' : ' '),
9809               (action & JOY_BUTTON_2 ? '2' : ' '));
9810       }
9811     }
9812
9813     Print("(%03d) ", tape->pos[i].delay);
9814     Print("[%05d]\n", tape_frame_counter);
9815
9816     tape_frame_counter += tape->pos[i].delay;
9817   }
9818
9819   PrintLine("-", 79);
9820 }
9821
9822 void DumpTapes(void)
9823 {
9824   static LevelDirTree *dumptape_leveldir = NULL;
9825
9826   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9827                                                 global.dumptape_leveldir);
9828
9829   if (dumptape_leveldir == NULL)
9830     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9831
9832   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9833       global.dumptape_level_nr > dumptape_leveldir->last_level)
9834     Fail("no such level number: %d", global.dumptape_level_nr);
9835
9836   leveldir_current = dumptape_leveldir;
9837
9838   if (options.mytapes)
9839     LoadTape(global.dumptape_level_nr);
9840   else
9841     LoadSolutionTape(global.dumptape_level_nr);
9842
9843   DumpTape(&tape);
9844
9845   CloseAllAndExit(0);
9846 }
9847
9848
9849 // ============================================================================
9850 // score file functions
9851 // ============================================================================
9852
9853 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9854 {
9855   int i;
9856
9857   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9858   {
9859     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9860     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9861     scores->entry[i].score = 0;
9862     scores->entry[i].time = 0;
9863
9864     scores->entry[i].id = -1;
9865     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9866     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9867     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9868     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9869     strcpy(scores->entry[i].country_code, "??");
9870   }
9871
9872   scores->num_entries = 0;
9873   scores->last_added = -1;
9874   scores->last_added_local = -1;
9875
9876   scores->updated = FALSE;
9877   scores->uploaded = FALSE;
9878   scores->tape_downloaded = FALSE;
9879   scores->force_last_added = FALSE;
9880
9881   // The following values are intentionally not reset here:
9882   // - last_level_nr
9883   // - last_entry_nr
9884   // - next_level_nr
9885   // - continue_playing
9886   // - continue_on_return
9887 }
9888
9889 static void setScoreInfoToDefaults(void)
9890 {
9891   setScoreInfoToDefaultsExt(&scores);
9892 }
9893
9894 static void setServerScoreInfoToDefaults(void)
9895 {
9896   setScoreInfoToDefaultsExt(&server_scores);
9897 }
9898
9899 static void LoadScore_OLD(int nr)
9900 {
9901   int i;
9902   char *filename = getScoreFilename(nr);
9903   char cookie[MAX_LINE_LEN];
9904   char line[MAX_LINE_LEN];
9905   char *line_ptr;
9906   FILE *file;
9907
9908   if (!(file = fopen(filename, MODE_READ)))
9909     return;
9910
9911   // check file identifier
9912   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9913     cookie[0] = '\0';
9914   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9915     cookie[strlen(cookie) - 1] = '\0';
9916
9917   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9918   {
9919     Warn("unknown format of score file '%s'", filename);
9920
9921     fclose(file);
9922
9923     return;
9924   }
9925
9926   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9927   {
9928     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9929       Warn("fscanf() failed; %s", strerror(errno));
9930
9931     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9932       line[0] = '\0';
9933
9934     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9935       line[strlen(line) - 1] = '\0';
9936
9937     for (line_ptr = line; *line_ptr; line_ptr++)
9938     {
9939       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9940       {
9941         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9942         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9943         break;
9944       }
9945     }
9946   }
9947
9948   fclose(file);
9949 }
9950
9951 static void ConvertScore_OLD(void)
9952 {
9953   // only convert score to time for levels that rate playing time over score
9954   if (!level.rate_time_over_score)
9955     return;
9956
9957   // convert old score to playing time for score-less levels (like Supaplex)
9958   int time_final_max = 999;
9959   int i;
9960
9961   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9962   {
9963     int score = scores.entry[i].score;
9964
9965     if (score > 0 && score < time_final_max)
9966       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9967   }
9968 }
9969
9970 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9971 {
9972   scores->file_version = getFileVersion(file);
9973   scores->game_version = getFileVersion(file);
9974
9975   return chunk_size;
9976 }
9977
9978 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9979 {
9980   char *level_identifier = NULL;
9981   int level_identifier_size;
9982   int i;
9983
9984   level_identifier_size = getFile16BitBE(file);
9985
9986   level_identifier = checked_malloc(level_identifier_size);
9987
9988   for (i = 0; i < level_identifier_size; i++)
9989     level_identifier[i] = getFile8Bit(file);
9990
9991   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9992   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9993
9994   checked_free(level_identifier);
9995
9996   scores->level_nr = getFile16BitBE(file);
9997   scores->num_entries = getFile16BitBE(file);
9998
9999   chunk_size = 2 + level_identifier_size + 2 + 2;
10000
10001   return chunk_size;
10002 }
10003
10004 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
10005 {
10006   int i, j;
10007
10008   for (i = 0; i < scores->num_entries; i++)
10009   {
10010     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10011       scores->entry[i].name[j] = getFile8Bit(file);
10012
10013     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
10014   }
10015
10016   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
10017
10018   return chunk_size;
10019 }
10020
10021 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
10022 {
10023   int i;
10024
10025   for (i = 0; i < scores->num_entries; i++)
10026     scores->entry[i].score = getFile16BitBE(file);
10027
10028   chunk_size = scores->num_entries * 2;
10029
10030   return chunk_size;
10031 }
10032
10033 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
10034 {
10035   int i;
10036
10037   for (i = 0; i < scores->num_entries; i++)
10038     scores->entry[i].score = getFile32BitBE(file);
10039
10040   chunk_size = scores->num_entries * 4;
10041
10042   return chunk_size;
10043 }
10044
10045 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
10046 {
10047   int i;
10048
10049   for (i = 0; i < scores->num_entries; i++)
10050     scores->entry[i].time = getFile32BitBE(file);
10051
10052   chunk_size = scores->num_entries * 4;
10053
10054   return chunk_size;
10055 }
10056
10057 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
10058 {
10059   int i, j;
10060
10061   for (i = 0; i < scores->num_entries; i++)
10062   {
10063     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10064       scores->entry[i].tape_basename[j] = getFile8Bit(file);
10065
10066     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
10067   }
10068
10069   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10070
10071   return chunk_size;
10072 }
10073
10074 void LoadScore(int nr)
10075 {
10076   char *filename = getScoreFilename(nr);
10077   char cookie[MAX_LINE_LEN];
10078   char chunk_name[CHUNK_ID_LEN + 1];
10079   int chunk_size;
10080   boolean old_score_file_format = FALSE;
10081   File *file;
10082
10083   // always start with reliable default values
10084   setScoreInfoToDefaults();
10085
10086   if (!(file = openFile(filename, MODE_READ)))
10087     return;
10088
10089   getFileChunkBE(file, chunk_name, NULL);
10090   if (strEqual(chunk_name, "RND1"))
10091   {
10092     getFile32BitBE(file);               // not used
10093
10094     getFileChunkBE(file, chunk_name, NULL);
10095     if (!strEqual(chunk_name, "SCOR"))
10096     {
10097       Warn("unknown format of score file '%s'", filename);
10098
10099       closeFile(file);
10100
10101       return;
10102     }
10103   }
10104   else  // check for old file format with cookie string
10105   {
10106     strcpy(cookie, chunk_name);
10107     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10108       cookie[4] = '\0';
10109     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10110       cookie[strlen(cookie) - 1] = '\0';
10111
10112     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10113     {
10114       Warn("unknown format of score file '%s'", filename);
10115
10116       closeFile(file);
10117
10118       return;
10119     }
10120
10121     old_score_file_format = TRUE;
10122   }
10123
10124   if (old_score_file_format)
10125   {
10126     // score files from versions before 4.2.4.0 without chunk structure
10127     LoadScore_OLD(nr);
10128
10129     // convert score to time, if possible (mainly for Supaplex levels)
10130     ConvertScore_OLD();
10131   }
10132   else
10133   {
10134     static struct
10135     {
10136       char *name;
10137       int size;
10138       int (*loader)(File *, int, struct ScoreInfo *);
10139     }
10140     chunk_info[] =
10141     {
10142       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10143       { "INFO", -1,                     LoadScore_INFO },
10144       { "NAME", -1,                     LoadScore_NAME },
10145       { "SCOR", -1,                     LoadScore_SCOR },
10146       { "SC4R", -1,                     LoadScore_SC4R },
10147       { "TIME", -1,                     LoadScore_TIME },
10148       { "TAPE", -1,                     LoadScore_TAPE },
10149
10150       {  NULL,  0,                      NULL }
10151     };
10152
10153     while (getFileChunkBE(file, chunk_name, &chunk_size))
10154     {
10155       int i = 0;
10156
10157       while (chunk_info[i].name != NULL &&
10158              !strEqual(chunk_name, chunk_info[i].name))
10159         i++;
10160
10161       if (chunk_info[i].name == NULL)
10162       {
10163         Warn("unknown chunk '%s' in score file '%s'",
10164               chunk_name, filename);
10165
10166         ReadUnusedBytesFromFile(file, chunk_size);
10167       }
10168       else if (chunk_info[i].size != -1 &&
10169                chunk_info[i].size != chunk_size)
10170       {
10171         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10172               chunk_size, chunk_name, filename);
10173
10174         ReadUnusedBytesFromFile(file, chunk_size);
10175       }
10176       else
10177       {
10178         // call function to load this score chunk
10179         int chunk_size_expected =
10180           (chunk_info[i].loader)(file, chunk_size, &scores);
10181
10182         // the size of some chunks cannot be checked before reading other
10183         // chunks first (like "HEAD" and "BODY") that contain some header
10184         // information, so check them here
10185         if (chunk_size_expected != chunk_size)
10186         {
10187           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10188                 chunk_size, chunk_name, filename);
10189         }
10190       }
10191     }
10192   }
10193
10194   closeFile(file);
10195 }
10196
10197 #if ENABLE_HISTORIC_CHUNKS
10198 void SaveScore_OLD(int nr)
10199 {
10200   int i;
10201   char *filename = getScoreFilename(nr);
10202   FILE *file;
10203
10204   // used instead of "leveldir_current->subdir" (for network games)
10205   InitScoreDirectory(levelset.identifier);
10206
10207   if (!(file = fopen(filename, MODE_WRITE)))
10208   {
10209     Warn("cannot save score for level %d", nr);
10210
10211     return;
10212   }
10213
10214   fprintf(file, "%s\n\n", SCORE_COOKIE);
10215
10216   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10217     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10218
10219   fclose(file);
10220
10221   SetFilePermissions(filename, PERMS_PRIVATE);
10222 }
10223 #endif
10224
10225 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10226 {
10227   putFileVersion(file, scores->file_version);
10228   putFileVersion(file, scores->game_version);
10229 }
10230
10231 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10232 {
10233   int level_identifier_size = strlen(scores->level_identifier) + 1;
10234   int i;
10235
10236   putFile16BitBE(file, level_identifier_size);
10237
10238   for (i = 0; i < level_identifier_size; i++)
10239     putFile8Bit(file, scores->level_identifier[i]);
10240
10241   putFile16BitBE(file, scores->level_nr);
10242   putFile16BitBE(file, scores->num_entries);
10243 }
10244
10245 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10246 {
10247   int i, j;
10248
10249   for (i = 0; i < scores->num_entries; i++)
10250   {
10251     int name_size = strlen(scores->entry[i].name);
10252
10253     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10254       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10255   }
10256 }
10257
10258 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10259 {
10260   int i;
10261
10262   for (i = 0; i < scores->num_entries; i++)
10263     putFile16BitBE(file, scores->entry[i].score);
10264 }
10265
10266 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10267 {
10268   int i;
10269
10270   for (i = 0; i < scores->num_entries; i++)
10271     putFile32BitBE(file, scores->entry[i].score);
10272 }
10273
10274 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10275 {
10276   int i;
10277
10278   for (i = 0; i < scores->num_entries; i++)
10279     putFile32BitBE(file, scores->entry[i].time);
10280 }
10281
10282 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10283 {
10284   int i, j;
10285
10286   for (i = 0; i < scores->num_entries; i++)
10287   {
10288     int size = strlen(scores->entry[i].tape_basename);
10289
10290     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10291       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10292   }
10293 }
10294
10295 static void SaveScoreToFilename(char *filename)
10296 {
10297   FILE *file;
10298   int info_chunk_size;
10299   int name_chunk_size;
10300   int scor_chunk_size;
10301   int sc4r_chunk_size;
10302   int time_chunk_size;
10303   int tape_chunk_size;
10304   boolean has_large_score_values;
10305   int i;
10306
10307   if (!(file = fopen(filename, MODE_WRITE)))
10308   {
10309     Warn("cannot save score file '%s'", filename);
10310
10311     return;
10312   }
10313
10314   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10315   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10316   scor_chunk_size = scores.num_entries * 2;
10317   sc4r_chunk_size = scores.num_entries * 4;
10318   time_chunk_size = scores.num_entries * 4;
10319   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10320
10321   has_large_score_values = FALSE;
10322   for (i = 0; i < scores.num_entries; i++)
10323     if (scores.entry[i].score > 0xffff)
10324       has_large_score_values = TRUE;
10325
10326   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10327   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10328
10329   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10330   SaveScore_VERS(file, &scores);
10331
10332   putFileChunkBE(file, "INFO", info_chunk_size);
10333   SaveScore_INFO(file, &scores);
10334
10335   putFileChunkBE(file, "NAME", name_chunk_size);
10336   SaveScore_NAME(file, &scores);
10337
10338   if (has_large_score_values)
10339   {
10340     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10341     SaveScore_SC4R(file, &scores);
10342   }
10343   else
10344   {
10345     putFileChunkBE(file, "SCOR", scor_chunk_size);
10346     SaveScore_SCOR(file, &scores);
10347   }
10348
10349   putFileChunkBE(file, "TIME", time_chunk_size);
10350   SaveScore_TIME(file, &scores);
10351
10352   putFileChunkBE(file, "TAPE", tape_chunk_size);
10353   SaveScore_TAPE(file, &scores);
10354
10355   fclose(file);
10356
10357   SetFilePermissions(filename, PERMS_PRIVATE);
10358 }
10359
10360 void SaveScore(int nr)
10361 {
10362   char *filename = getScoreFilename(nr);
10363   int i;
10364
10365   // used instead of "leveldir_current->subdir" (for network games)
10366   InitScoreDirectory(levelset.identifier);
10367
10368   scores.file_version = FILE_VERSION_ACTUAL;
10369   scores.game_version = GAME_VERSION_ACTUAL;
10370
10371   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10372   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10373   scores.level_nr = level_nr;
10374
10375   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10376     if (scores.entry[i].score == 0 &&
10377         scores.entry[i].time == 0 &&
10378         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10379       break;
10380
10381   scores.num_entries = i;
10382
10383   if (scores.num_entries == 0)
10384     return;
10385
10386   SaveScoreToFilename(filename);
10387 }
10388
10389 static void LoadServerScoreFromCache(int nr)
10390 {
10391   struct ScoreEntry score_entry;
10392   struct
10393   {
10394     void *value;
10395     boolean is_string;
10396     int string_size;
10397   }
10398   score_mapping[] =
10399   {
10400     { &score_entry.score,               FALSE,  0                       },
10401     { &score_entry.time,                FALSE,  0                       },
10402     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10403     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10404     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10405     { &score_entry.id,                  FALSE,  0                       },
10406     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10407     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10408     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10409     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10410
10411     { NULL,                             FALSE,  0                       }
10412   };
10413   char *filename = getScoreCacheFilename(nr);
10414   SetupFileHash *score_hash = loadSetupFileHash(filename);
10415   int i, j;
10416
10417   server_scores.num_entries = 0;
10418
10419   if (score_hash == NULL)
10420     return;
10421
10422   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10423   {
10424     score_entry = server_scores.entry[i];
10425
10426     for (j = 0; score_mapping[j].value != NULL; j++)
10427     {
10428       char token[10];
10429
10430       sprintf(token, "%02d.%d", i, j);
10431
10432       char *value = getHashEntry(score_hash, token);
10433
10434       if (value == NULL)
10435         continue;
10436
10437       if (score_mapping[j].is_string)
10438       {
10439         char *score_value = (char *)score_mapping[j].value;
10440         int value_size = score_mapping[j].string_size;
10441
10442         strncpy(score_value, value, value_size);
10443         score_value[value_size] = '\0';
10444       }
10445       else
10446       {
10447         int *score_value = (int *)score_mapping[j].value;
10448
10449         *score_value = atoi(value);
10450       }
10451
10452       server_scores.num_entries = i + 1;
10453     }
10454
10455     server_scores.entry[i] = score_entry;
10456   }
10457
10458   freeSetupFileHash(score_hash);
10459 }
10460
10461 void LoadServerScore(int nr, boolean download_score)
10462 {
10463   if (!setup.use_api_server)
10464     return;
10465
10466   // always start with reliable default values
10467   setServerScoreInfoToDefaults();
10468
10469   // 1st step: load server scores from cache file (which may not exist)
10470   // (this should prevent reading it while the thread is writing to it)
10471   LoadServerScoreFromCache(nr);
10472
10473   if (download_score && runtime.use_api_server)
10474   {
10475     // 2nd step: download server scores from score server to cache file
10476     // (as thread, as it might time out if the server is not reachable)
10477     ApiGetScoreAsThread(nr);
10478   }
10479 }
10480
10481 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10482 {
10483   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10484
10485   // if score tape not uploaded, ask for uploading missing tapes later
10486   if (!setup.has_remaining_tapes)
10487     setup.ask_for_remaining_tapes = TRUE;
10488
10489   setup.provide_uploading_tapes = TRUE;
10490   setup.has_remaining_tapes = TRUE;
10491
10492   SaveSetup_ServerSetup();
10493 }
10494
10495 void SaveServerScore(int nr, boolean tape_saved)
10496 {
10497   if (!runtime.use_api_server)
10498   {
10499     PrepareScoreTapesForUpload(leveldir_current->subdir);
10500
10501     return;
10502   }
10503
10504   ApiAddScoreAsThread(nr, tape_saved, NULL);
10505 }
10506
10507 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10508                              char *score_tape_filename)
10509 {
10510   if (!runtime.use_api_server)
10511     return;
10512
10513   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10514 }
10515
10516 void LoadLocalAndServerScore(int nr, boolean download_score)
10517 {
10518   int last_added_local = scores.last_added_local;
10519   boolean force_last_added = scores.force_last_added;
10520
10521   // needed if only showing server scores
10522   setScoreInfoToDefaults();
10523
10524   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10525     LoadScore(nr);
10526
10527   // restore last added local score entry (before merging server scores)
10528   scores.last_added = scores.last_added_local = last_added_local;
10529
10530   if (setup.use_api_server &&
10531       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10532   {
10533     // load server scores from cache file and trigger update from server
10534     LoadServerScore(nr, download_score);
10535
10536     // merge local scores with scores from server
10537     MergeServerScore();
10538   }
10539
10540   if (force_last_added)
10541     scores.force_last_added = force_last_added;
10542 }
10543
10544
10545 // ============================================================================
10546 // setup file functions
10547 // ============================================================================
10548
10549 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10550
10551
10552 static struct TokenInfo global_setup_tokens[] =
10553 {
10554   {
10555     TYPE_STRING,
10556     &setup.player_name,                         "player_name"
10557   },
10558   {
10559     TYPE_SWITCH,
10560     &setup.multiple_users,                      "multiple_users"
10561   },
10562   {
10563     TYPE_SWITCH,
10564     &setup.sound,                               "sound"
10565   },
10566   {
10567     TYPE_SWITCH,
10568     &setup.sound_loops,                         "repeating_sound_loops"
10569   },
10570   {
10571     TYPE_SWITCH,
10572     &setup.sound_music,                         "background_music"
10573   },
10574   {
10575     TYPE_SWITCH,
10576     &setup.sound_simple,                        "simple_sound_effects"
10577   },
10578   {
10579     TYPE_SWITCH,
10580     &setup.toons,                               "toons"
10581   },
10582   {
10583     TYPE_SWITCH,
10584     &setup.global_animations,                   "global_animations"
10585   },
10586   {
10587     TYPE_SWITCH,
10588     &setup.scroll_delay,                        "scroll_delay"
10589   },
10590   {
10591     TYPE_SWITCH,
10592     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10593   },
10594   {
10595     TYPE_INTEGER,
10596     &setup.scroll_delay_value,                  "scroll_delay_value"
10597   },
10598   {
10599     TYPE_STRING,
10600     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10601   },
10602   {
10603     TYPE_INTEGER,
10604     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10605   },
10606   {
10607     TYPE_SWITCH,
10608     &setup.fade_screens,                        "fade_screens"
10609   },
10610   {
10611     TYPE_SWITCH,
10612     &setup.autorecord,                          "automatic_tape_recording"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.autorecord_after_replay,             "autorecord_after_replay"
10617   },
10618   {
10619     TYPE_SWITCH,
10620     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10621   },
10622   {
10623     TYPE_SWITCH,
10624     &setup.show_titlescreen,                    "show_titlescreen"
10625   },
10626   {
10627     TYPE_SWITCH,
10628     &setup.quick_doors,                         "quick_doors"
10629   },
10630   {
10631     TYPE_SWITCH,
10632     &setup.team_mode,                           "team_mode"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.handicap,                            "handicap"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.skip_levels,                         "skip_levels"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.increment_levels,                    "increment_levels"
10645   },
10646   {
10647     TYPE_SWITCH,
10648     &setup.auto_play_next_level,                "auto_play_next_level"
10649   },
10650   {
10651     TYPE_SWITCH,
10652     &setup.count_score_after_game,              "count_score_after_game"
10653   },
10654   {
10655     TYPE_SWITCH,
10656     &setup.show_scores_after_game,              "show_scores_after_game"
10657   },
10658   {
10659     TYPE_SWITCH,
10660     &setup.time_limit,                          "time_limit"
10661   },
10662   {
10663     TYPE_SWITCH,
10664     &setup.fullscreen,                          "fullscreen"
10665   },
10666   {
10667     TYPE_INTEGER,
10668     &setup.window_scaling_percent,              "window_scaling_percent"
10669   },
10670   {
10671     TYPE_STRING,
10672     &setup.window_scaling_quality,              "window_scaling_quality"
10673   },
10674   {
10675     TYPE_STRING,
10676     &setup.screen_rendering_mode,               "screen_rendering_mode"
10677   },
10678   {
10679     TYPE_STRING,
10680     &setup.vsync_mode,                          "vsync_mode"
10681   },
10682   {
10683     TYPE_SWITCH,
10684     &setup.ask_on_escape,                       "ask_on_escape"
10685   },
10686   {
10687     TYPE_SWITCH,
10688     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10689   },
10690   {
10691     TYPE_SWITCH,
10692     &setup.ask_on_game_over,                    "ask_on_game_over"
10693   },
10694   {
10695     TYPE_SWITCH,
10696     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10697   },
10698   {
10699     TYPE_SWITCH,
10700     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10701   },
10702   {
10703     TYPE_SWITCH,
10704     &setup.quick_switch,                        "quick_player_switch"
10705   },
10706   {
10707     TYPE_SWITCH,
10708     &setup.input_on_focus,                      "input_on_focus"
10709   },
10710   {
10711     TYPE_SWITCH,
10712     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10713   },
10714   {
10715     TYPE_SWITCH,
10716     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10717   },
10718   {
10719     TYPE_SWITCH,
10720     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10721   },
10722   {
10723     TYPE_SWITCH,
10724     &setup.game_speed_extended,                 "game_speed_extended"
10725   },
10726   {
10727     TYPE_INTEGER,
10728     &setup.game_frame_delay,                    "game_frame_delay"
10729   },
10730   {
10731     TYPE_SWITCH,
10732     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10733   },
10734   {
10735     TYPE_SWITCH,
10736     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10737   },
10738   {
10739     TYPE_SWITCH,
10740     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10741   },
10742   {
10743     TYPE_SWITCH3,
10744     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10745   },
10746   {
10747     TYPE_INTEGER,
10748     &setup.bd_palette_c64,                      "bd_palette_c64"
10749   },
10750   {
10751     TYPE_INTEGER,
10752     &setup.bd_palette_c64dtv,                   "bd_palette_c64dtv"
10753   },
10754   {
10755     TYPE_INTEGER,
10756     &setup.bd_palette_atari,                    "bd_palette_atari"
10757   },
10758   {
10759     TYPE_INTEGER,
10760     &setup.bd_default_color_type,               "bd_default_color_type"
10761   },
10762   {
10763     TYPE_SWITCH,
10764     &setup.sp_show_border_elements,             "sp_show_border_elements"
10765   },
10766   {
10767     TYPE_SWITCH,
10768     &setup.small_game_graphics,                 "small_game_graphics"
10769   },
10770   {
10771     TYPE_SWITCH,
10772     &setup.show_load_save_buttons,              "show_load_save_buttons"
10773   },
10774   {
10775     TYPE_SWITCH,
10776     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10777   },
10778   {
10779     TYPE_STRING,
10780     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10781   },
10782   {
10783     TYPE_STRING,
10784     &setup.graphics_set,                        "graphics_set"
10785   },
10786   {
10787     TYPE_STRING,
10788     &setup.sounds_set,                          "sounds_set"
10789   },
10790   {
10791     TYPE_STRING,
10792     &setup.music_set,                           "music_set"
10793   },
10794   {
10795     TYPE_SWITCH3,
10796     &setup.override_level_graphics,             "override_level_graphics"
10797   },
10798   {
10799     TYPE_SWITCH3,
10800     &setup.override_level_sounds,               "override_level_sounds"
10801   },
10802   {
10803     TYPE_SWITCH3,
10804     &setup.override_level_music,                "override_level_music"
10805   },
10806   {
10807     TYPE_INTEGER,
10808     &setup.volume_simple,                       "volume_simple"
10809   },
10810   {
10811     TYPE_INTEGER,
10812     &setup.volume_loops,                        "volume_loops"
10813   },
10814   {
10815     TYPE_INTEGER,
10816     &setup.volume_music,                        "volume_music"
10817   },
10818   {
10819     TYPE_SWITCH,
10820     &setup.network_mode,                        "network_mode"
10821   },
10822   {
10823     TYPE_PLAYER,
10824     &setup.network_player_nr,                   "network_player"
10825   },
10826   {
10827     TYPE_STRING,
10828     &setup.network_server_hostname,             "network_server_hostname"
10829   },
10830   {
10831     TYPE_STRING,
10832     &setup.touch.control_type,                  "touch.control_type"
10833   },
10834   {
10835     TYPE_INTEGER,
10836     &setup.touch.move_distance,                 "touch.move_distance"
10837   },
10838   {
10839     TYPE_INTEGER,
10840     &setup.touch.drop_distance,                 "touch.drop_distance"
10841   },
10842   {
10843     TYPE_INTEGER,
10844     &setup.touch.transparency,                  "touch.transparency"
10845   },
10846   {
10847     TYPE_INTEGER,
10848     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10849   },
10850   {
10851     TYPE_INTEGER,
10852     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10853   },
10854   {
10855     TYPE_INTEGER,
10856     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10857   },
10858   {
10859     TYPE_INTEGER,
10860     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10861   },
10862   {
10863     TYPE_INTEGER,
10864     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10865   },
10866   {
10867     TYPE_INTEGER,
10868     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10869   },
10870   {
10871     TYPE_SWITCH,
10872     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10873   },
10874 };
10875
10876 static struct TokenInfo auto_setup_tokens[] =
10877 {
10878   {
10879     TYPE_INTEGER,
10880     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10881   },
10882 };
10883
10884 static struct TokenInfo server_setup_tokens[] =
10885 {
10886   {
10887     TYPE_STRING,
10888     &setup.player_uuid,                         "player_uuid"
10889   },
10890   {
10891     TYPE_INTEGER,
10892     &setup.player_version,                      "player_version"
10893   },
10894   {
10895     TYPE_SWITCH,
10896     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10897   },
10898   {
10899     TYPE_STRING,
10900     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10901   },
10902   {
10903     TYPE_STRING,
10904     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10905   },
10906   {
10907     TYPE_SWITCH,
10908     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10909   },
10910   {
10911     TYPE_SWITCH,
10912     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10913   },
10914   {
10915     TYPE_SWITCH,
10916     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10917   },
10918   {
10919     TYPE_SWITCH,
10920     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10921   },
10922   {
10923     TYPE_SWITCH,
10924     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10925   },
10926 };
10927
10928 static struct TokenInfo editor_setup_tokens[] =
10929 {
10930   {
10931     TYPE_SWITCH,
10932     &setup.editor.el_classic,                   "editor.el_classic"
10933   },
10934   {
10935     TYPE_SWITCH,
10936     &setup.editor.el_custom,                    "editor.el_custom"
10937   },
10938   {
10939     TYPE_SWITCH,
10940     &setup.editor.el_user_defined,              "editor.el_user_defined"
10941   },
10942   {
10943     TYPE_SWITCH,
10944     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10945   },
10946   {
10947     TYPE_SWITCH,
10948     &setup.editor.el_headlines,                 "editor.el_headlines"
10949   },
10950   {
10951     TYPE_SWITCH,
10952     &setup.editor.show_element_token,           "editor.show_element_token"
10953   },
10954   {
10955     TYPE_SWITCH,
10956     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10957   },
10958 };
10959
10960 static struct TokenInfo editor_cascade_setup_tokens[] =
10961 {
10962   {
10963     TYPE_SWITCH,
10964     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10965   },
10966   {
10967     TYPE_SWITCH,
10968     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10969   },
10970   {
10971     TYPE_SWITCH,
10972     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10973   },
10974   {
10975     TYPE_SWITCH,
10976     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10977   },
10978   {
10979     TYPE_SWITCH,
10980     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10981   },
10982   {
10983     TYPE_SWITCH,
10984     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10985   },
10986   {
10987     TYPE_SWITCH,
10988     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10989   },
10990   {
10991     TYPE_SWITCH,
10992     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10993   },
10994   {
10995     TYPE_SWITCH,
10996     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10997   },
10998   {
10999     TYPE_SWITCH,
11000     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
11001   },
11002   {
11003     TYPE_SWITCH,
11004     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
11005   },
11006   {
11007     TYPE_SWITCH,
11008     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
11009   },
11010   {
11011     TYPE_SWITCH,
11012     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
11013   },
11014   {
11015     TYPE_SWITCH,
11016     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
11017   },
11018   {
11019     TYPE_SWITCH,
11020     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
11021   },
11022   {
11023     TYPE_SWITCH,
11024     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
11025   },
11026   {
11027     TYPE_SWITCH,
11028     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
11029   },
11030   {
11031     TYPE_SWITCH,
11032     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
11033   },
11034   {
11035     TYPE_SWITCH,
11036     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
11037   },
11038   {
11039     TYPE_SWITCH,
11040     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
11041   },
11042 };
11043
11044 static struct TokenInfo shortcut_setup_tokens[] =
11045 {
11046   {
11047     TYPE_KEY_X11,
11048     &setup.shortcut.save_game,                  "shortcut.save_game"
11049   },
11050   {
11051     TYPE_KEY_X11,
11052     &setup.shortcut.load_game,                  "shortcut.load_game"
11053   },
11054   {
11055     TYPE_KEY_X11,
11056     &setup.shortcut.restart_game,               "shortcut.restart_game"
11057   },
11058   {
11059     TYPE_KEY_X11,
11060     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
11061   },
11062   {
11063     TYPE_KEY_X11,
11064     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
11065   },
11066   {
11067     TYPE_KEY_X11,
11068     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
11069   },
11070   {
11071     TYPE_KEY_X11,
11072     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
11073   },
11074   {
11075     TYPE_KEY_X11,
11076     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
11077   },
11078   {
11079     TYPE_KEY_X11,
11080     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
11081   },
11082   {
11083     TYPE_KEY_X11,
11084     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
11085   },
11086   {
11087     TYPE_KEY_X11,
11088     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
11089   },
11090   {
11091     TYPE_KEY_X11,
11092     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11093   },
11094   {
11095     TYPE_KEY_X11,
11096     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11097   },
11098   {
11099     TYPE_KEY_X11,
11100     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11101   },
11102   {
11103     TYPE_KEY_X11,
11104     &setup.shortcut.tape_record,                "shortcut.tape_record"
11105   },
11106   {
11107     TYPE_KEY_X11,
11108     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11109   },
11110   {
11111     TYPE_KEY_X11,
11112     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11113   },
11114   {
11115     TYPE_KEY_X11,
11116     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11117   },
11118   {
11119     TYPE_KEY_X11,
11120     &setup.shortcut.sound_music,                "shortcut.sound_music"
11121   },
11122   {
11123     TYPE_KEY_X11,
11124     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11125   },
11126   {
11127     TYPE_KEY_X11,
11128     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11129   },
11130   {
11131     TYPE_KEY_X11,
11132     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11133   },
11134   {
11135     TYPE_KEY_X11,
11136     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11137   },
11138 };
11139
11140 static struct SetupInputInfo setup_input;
11141 static struct TokenInfo player_setup_tokens[] =
11142 {
11143   {
11144     TYPE_BOOLEAN,
11145     &setup_input.use_joystick,                  ".use_joystick"
11146   },
11147   {
11148     TYPE_STRING,
11149     &setup_input.joy.device_name,               ".joy.device_name"
11150   },
11151   {
11152     TYPE_INTEGER,
11153     &setup_input.joy.xleft,                     ".joy.xleft"
11154   },
11155   {
11156     TYPE_INTEGER,
11157     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11158   },
11159   {
11160     TYPE_INTEGER,
11161     &setup_input.joy.xright,                    ".joy.xright"
11162   },
11163   {
11164     TYPE_INTEGER,
11165     &setup_input.joy.yupper,                    ".joy.yupper"
11166   },
11167   {
11168     TYPE_INTEGER,
11169     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11170   },
11171   {
11172     TYPE_INTEGER,
11173     &setup_input.joy.ylower,                    ".joy.ylower"
11174   },
11175   {
11176     TYPE_INTEGER,
11177     &setup_input.joy.snap,                      ".joy.snap_field"
11178   },
11179   {
11180     TYPE_INTEGER,
11181     &setup_input.joy.drop,                      ".joy.place_bomb"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup_input.key.left,                      ".key.move_left"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup_input.key.right,                     ".key.move_right"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup_input.key.up,                        ".key.move_up"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup_input.key.down,                      ".key.move_down"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup_input.key.snap,                      ".key.snap_field"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup_input.key.drop,                      ".key.place_bomb"
11206   },
11207 };
11208
11209 static struct TokenInfo system_setup_tokens[] =
11210 {
11211   {
11212     TYPE_STRING,
11213     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11214   },
11215   {
11216     TYPE_STRING,
11217     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11218   },
11219   {
11220     TYPE_STRING,
11221     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11222   },
11223   {
11224     TYPE_INTEGER,
11225     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11226   },
11227 };
11228
11229 static struct TokenInfo internal_setup_tokens[] =
11230 {
11231   {
11232     TYPE_STRING,
11233     &setup.internal.program_title,              "program_title"
11234   },
11235   {
11236     TYPE_STRING,
11237     &setup.internal.program_version,            "program_version"
11238   },
11239   {
11240     TYPE_STRING,
11241     &setup.internal.program_author,             "program_author"
11242   },
11243   {
11244     TYPE_STRING,
11245     &setup.internal.program_email,              "program_email"
11246   },
11247   {
11248     TYPE_STRING,
11249     &setup.internal.program_website,            "program_website"
11250   },
11251   {
11252     TYPE_STRING,
11253     &setup.internal.program_copyright,          "program_copyright"
11254   },
11255   {
11256     TYPE_STRING,
11257     &setup.internal.program_company,            "program_company"
11258   },
11259   {
11260     TYPE_STRING,
11261     &setup.internal.program_icon_file,          "program_icon_file"
11262   },
11263   {
11264     TYPE_STRING,
11265     &setup.internal.default_graphics_set,       "default_graphics_set"
11266   },
11267   {
11268     TYPE_STRING,
11269     &setup.internal.default_sounds_set,         "default_sounds_set"
11270   },
11271   {
11272     TYPE_STRING,
11273     &setup.internal.default_music_set,          "default_music_set"
11274   },
11275   {
11276     TYPE_STRING,
11277     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11278   },
11279   {
11280     TYPE_STRING,
11281     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11282   },
11283   {
11284     TYPE_STRING,
11285     &setup.internal.fallback_music_file,        "fallback_music_file"
11286   },
11287   {
11288     TYPE_STRING,
11289     &setup.internal.default_level_series,       "default_level_series"
11290   },
11291   {
11292     TYPE_INTEGER,
11293     &setup.internal.default_window_width,       "default_window_width"
11294   },
11295   {
11296     TYPE_INTEGER,
11297     &setup.internal.default_window_height,      "default_window_height"
11298   },
11299   {
11300     TYPE_BOOLEAN,
11301     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11302   },
11303   {
11304     TYPE_BOOLEAN,
11305     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11306   },
11307   {
11308     TYPE_BOOLEAN,
11309     &setup.internal.create_user_levelset,       "create_user_levelset"
11310   },
11311   {
11312     TYPE_BOOLEAN,
11313     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11314   },
11315   {
11316     TYPE_BOOLEAN,
11317     &setup.internal.menu_game,                  "menu_game"
11318   },
11319   {
11320     TYPE_BOOLEAN,
11321     &setup.internal.menu_engines,               "menu_engines"
11322   },
11323   {
11324     TYPE_BOOLEAN,
11325     &setup.internal.menu_editor,                "menu_editor"
11326   },
11327   {
11328     TYPE_BOOLEAN,
11329     &setup.internal.menu_graphics,              "menu_graphics"
11330   },
11331   {
11332     TYPE_BOOLEAN,
11333     &setup.internal.menu_sound,                 "menu_sound"
11334   },
11335   {
11336     TYPE_BOOLEAN,
11337     &setup.internal.menu_artwork,               "menu_artwork"
11338   },
11339   {
11340     TYPE_BOOLEAN,
11341     &setup.internal.menu_input,                 "menu_input"
11342   },
11343   {
11344     TYPE_BOOLEAN,
11345     &setup.internal.menu_touch,                 "menu_touch"
11346   },
11347   {
11348     TYPE_BOOLEAN,
11349     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11350   },
11351   {
11352     TYPE_BOOLEAN,
11353     &setup.internal.menu_exit,                  "menu_exit"
11354   },
11355   {
11356     TYPE_BOOLEAN,
11357     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11358   },
11359   {
11360     TYPE_BOOLEAN,
11361     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11362   },
11363   {
11364     TYPE_BOOLEAN,
11365     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11366   },
11367   {
11368     TYPE_BOOLEAN,
11369     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11370   },
11371   {
11372     TYPE_BOOLEAN,
11373     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11374   },
11375   {
11376     TYPE_BOOLEAN,
11377     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11378   },
11379   {
11380     TYPE_BOOLEAN,
11381     &setup.internal.info_title,                 "info_title"
11382   },
11383   {
11384     TYPE_BOOLEAN,
11385     &setup.internal.info_elements,              "info_elements"
11386   },
11387   {
11388     TYPE_BOOLEAN,
11389     &setup.internal.info_music,                 "info_music"
11390   },
11391   {
11392     TYPE_BOOLEAN,
11393     &setup.internal.info_credits,               "info_credits"
11394   },
11395   {
11396     TYPE_BOOLEAN,
11397     &setup.internal.info_program,               "info_program"
11398   },
11399   {
11400     TYPE_BOOLEAN,
11401     &setup.internal.info_version,               "info_version"
11402   },
11403   {
11404     TYPE_BOOLEAN,
11405     &setup.internal.info_levelset,              "info_levelset"
11406   },
11407   {
11408     TYPE_BOOLEAN,
11409     &setup.internal.info_exit,                  "info_exit"
11410   },
11411 };
11412
11413 static struct TokenInfo debug_setup_tokens[] =
11414 {
11415   {
11416     TYPE_INTEGER,
11417     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11418   },
11419   {
11420     TYPE_INTEGER,
11421     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11422   },
11423   {
11424     TYPE_INTEGER,
11425     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11426   },
11427   {
11428     TYPE_INTEGER,
11429     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11430   },
11431   {
11432     TYPE_INTEGER,
11433     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11434   },
11435   {
11436     TYPE_INTEGER,
11437     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11438   },
11439   {
11440     TYPE_INTEGER,
11441     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11442   },
11443   {
11444     TYPE_INTEGER,
11445     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11446   },
11447   {
11448     TYPE_INTEGER,
11449     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11450   },
11451   {
11452     TYPE_INTEGER,
11453     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11454   },
11455   {
11456     TYPE_KEY_X11,
11457     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11458   },
11459   {
11460     TYPE_KEY_X11,
11461     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11462   },
11463   {
11464     TYPE_KEY_X11,
11465     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11466   },
11467   {
11468     TYPE_KEY_X11,
11469     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11470   },
11471   {
11472     TYPE_KEY_X11,
11473     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11474   },
11475   {
11476     TYPE_KEY_X11,
11477     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11478   },
11479   {
11480     TYPE_KEY_X11,
11481     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11482   },
11483   {
11484     TYPE_KEY_X11,
11485     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11486   },
11487   {
11488     TYPE_KEY_X11,
11489     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11490   },
11491   {
11492     TYPE_KEY_X11,
11493     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11494   },
11495   {
11496     TYPE_BOOLEAN,
11497     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11498   {
11499     TYPE_BOOLEAN,
11500     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11501   },
11502   {
11503     TYPE_BOOLEAN,
11504     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11505   },
11506   {
11507     TYPE_SWITCH3,
11508     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11509   },
11510   {
11511     TYPE_INTEGER,
11512     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11513   },
11514 };
11515
11516 static struct TokenInfo options_setup_tokens[] =
11517 {
11518   {
11519     TYPE_BOOLEAN,
11520     &setup.options.verbose,                     "options.verbose"
11521   },
11522   {
11523     TYPE_BOOLEAN,
11524     &setup.options.debug,                       "options.debug"
11525   },
11526   {
11527     TYPE_STRING,
11528     &setup.options.debug_mode,                  "options.debug_mode"
11529   },
11530 };
11531
11532 static void setSetupInfoToDefaults(struct SetupInfo *si)
11533 {
11534   int i;
11535
11536   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11537
11538   si->multiple_users = TRUE;
11539
11540   si->sound = TRUE;
11541   si->sound_loops = TRUE;
11542   si->sound_music = TRUE;
11543   si->sound_simple = TRUE;
11544   si->toons = TRUE;
11545   si->global_animations = TRUE;
11546   si->scroll_delay = TRUE;
11547   si->forced_scroll_delay = FALSE;
11548   si->scroll_delay_value = STD_SCROLL_DELAY;
11549   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11550   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11551   si->fade_screens = TRUE;
11552   si->autorecord = TRUE;
11553   si->autorecord_after_replay = TRUE;
11554   si->auto_pause_on_start = FALSE;
11555   si->show_titlescreen = TRUE;
11556   si->quick_doors = FALSE;
11557   si->team_mode = FALSE;
11558   si->handicap = TRUE;
11559   si->skip_levels = TRUE;
11560   si->increment_levels = TRUE;
11561   si->auto_play_next_level = TRUE;
11562   si->count_score_after_game = TRUE;
11563   si->show_scores_after_game = TRUE;
11564   si->time_limit = TRUE;
11565   si->fullscreen = FALSE;
11566   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11567   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11568   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11569   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11570   si->ask_on_escape = TRUE;
11571   si->ask_on_escape_editor = TRUE;
11572   si->ask_on_game_over = TRUE;
11573   si->ask_on_quit_game = TRUE;
11574   si->ask_on_quit_program = TRUE;
11575   si->quick_switch = FALSE;
11576   si->input_on_focus = FALSE;
11577   si->prefer_aga_graphics = TRUE;
11578   si->prefer_lowpass_sounds = FALSE;
11579   si->prefer_extra_panel_items = TRUE;
11580   si->game_speed_extended = FALSE;
11581   si->game_frame_delay = GAME_FRAME_DELAY;
11582   si->bd_skip_uncovering = FALSE;
11583   si->bd_skip_hatching = FALSE;
11584   si->bd_scroll_delay = TRUE;
11585   si->bd_smooth_movements = AUTO;
11586   si->bd_palette_c64 = GD_DEFAULT_PALETTE_C64;
11587   si->bd_palette_c64dtv = GD_DEFAULT_PALETTE_C64DTV;
11588   si->bd_palette_atari = GD_DEFAULT_PALETTE_ATARI;
11589   si->bd_default_color_type = GD_DEFAULT_COLOR_TYPE;
11590   si->sp_show_border_elements = FALSE;
11591   si->small_game_graphics = FALSE;
11592   si->show_load_save_buttons = FALSE;
11593   si->show_undo_redo_buttons = FALSE;
11594   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11595
11596   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11597   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11598   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11599
11600   si->override_level_graphics = FALSE;
11601   si->override_level_sounds = FALSE;
11602   si->override_level_music = FALSE;
11603
11604   si->volume_simple = 100;              // percent
11605   si->volume_loops = 100;               // percent
11606   si->volume_music = 100;               // percent
11607
11608   si->network_mode = FALSE;
11609   si->network_player_nr = 0;            // first player
11610   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11611
11612   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11613   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11614   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11615   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11616   si->touch.draw_outlined = TRUE;
11617   si->touch.draw_pressed = TRUE;
11618
11619   for (i = 0; i < 2; i++)
11620   {
11621     char *default_grid_button[6][2] =
11622     {
11623       { "      ", "  ^^  " },
11624       { "      ", "  ^^  " },
11625       { "      ", "<<  >>" },
11626       { "      ", "<<  >>" },
11627       { "111222", "  vv  " },
11628       { "111222", "  vv  " }
11629     };
11630     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11631     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11632     int min_xsize = MIN(6, grid_xsize);
11633     int min_ysize = MIN(6, grid_ysize);
11634     int startx = grid_xsize - min_xsize;
11635     int starty = grid_ysize - min_ysize;
11636     int x, y;
11637
11638     // virtual buttons grid can only be set to defaults if video is initialized
11639     // (this will be repeated if virtual buttons are not loaded from setup file)
11640     if (video.initialized)
11641     {
11642       si->touch.grid_xsize[i] = grid_xsize;
11643       si->touch.grid_ysize[i] = grid_ysize;
11644     }
11645     else
11646     {
11647       si->touch.grid_xsize[i] = -1;
11648       si->touch.grid_ysize[i] = -1;
11649     }
11650
11651     for (x = 0; x < MAX_GRID_XSIZE; x++)
11652       for (y = 0; y < MAX_GRID_YSIZE; y++)
11653         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11654
11655     for (x = 0; x < min_xsize; x++)
11656       for (y = 0; y < min_ysize; y++)
11657         si->touch.grid_button[i][x][starty + y] =
11658           default_grid_button[y][0][x];
11659
11660     for (x = 0; x < min_xsize; x++)
11661       for (y = 0; y < min_ysize; y++)
11662         si->touch.grid_button[i][startx + x][starty + y] =
11663           default_grid_button[y][1][x];
11664   }
11665
11666   si->touch.grid_initialized            = video.initialized;
11667
11668   si->touch.overlay_buttons             = FALSE;
11669
11670   si->editor.el_boulderdash             = TRUE;
11671   si->editor.el_boulderdash_native      = TRUE;
11672   si->editor.el_boulderdash_effects     = TRUE;
11673   si->editor.el_emerald_mine            = TRUE;
11674   si->editor.el_emerald_mine_club       = TRUE;
11675   si->editor.el_more                    = TRUE;
11676   si->editor.el_sokoban                 = TRUE;
11677   si->editor.el_supaplex                = TRUE;
11678   si->editor.el_diamond_caves           = TRUE;
11679   si->editor.el_dx_boulderdash          = TRUE;
11680
11681   si->editor.el_mirror_magic            = TRUE;
11682   si->editor.el_deflektor               = TRUE;
11683
11684   si->editor.el_chars                   = TRUE;
11685   si->editor.el_steel_chars             = TRUE;
11686
11687   si->editor.el_classic                 = TRUE;
11688   si->editor.el_custom                  = TRUE;
11689
11690   si->editor.el_user_defined            = FALSE;
11691   si->editor.el_dynamic                 = TRUE;
11692
11693   si->editor.el_headlines               = TRUE;
11694
11695   si->editor.show_element_token         = FALSE;
11696
11697   si->editor.show_read_only_warning     = TRUE;
11698
11699   si->editor.use_template_for_new_levels = TRUE;
11700
11701   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11702   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11703   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11704   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11705   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11706
11707   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11708   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11709   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11710   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11711   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11712
11713   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11714   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11715   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11716   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11717   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11718   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11719
11720   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11721   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11722   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11723
11724   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11725   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11726   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11727   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11728
11729   for (i = 0; i < MAX_PLAYERS; i++)
11730   {
11731     si->input[i].use_joystick = FALSE;
11732     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11733     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11734     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11735     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11736     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11737     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11738     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11739     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11740     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11741     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11742     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11743     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11744     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11745     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11746     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11747   }
11748
11749   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11750   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11751   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11752   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11753
11754   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11755   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11756   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11757   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11758   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11759   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11760   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11761
11762   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11763
11764   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11765   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11766   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11767
11768   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11769   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11770   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11771
11772   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11773   si->internal.choose_from_top_leveldir = FALSE;
11774   si->internal.show_scaling_in_title = TRUE;
11775   si->internal.create_user_levelset = TRUE;
11776   si->internal.info_screens_from_main = FALSE;
11777
11778   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11779   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11780
11781   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11782   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11783   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11784   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11785   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11786   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11787   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11788   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11789   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11790   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11791
11792   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11793   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11794   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11795   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11796   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11797   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11798   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11799   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11800   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11801   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11802
11803   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11804   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11805
11806   si->debug.show_frames_per_second = FALSE;
11807
11808   si->debug.xsn_mode = AUTO;
11809   si->debug.xsn_percent = 0;
11810
11811   si->options.verbose = FALSE;
11812   si->options.debug = FALSE;
11813   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11814
11815 #if defined(PLATFORM_ANDROID)
11816   si->fullscreen = TRUE;
11817   si->touch.overlay_buttons = TRUE;
11818 #endif
11819
11820   setHideSetupEntry(&setup.debug.xsn_mode);
11821 }
11822
11823 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11824 {
11825   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11826 }
11827
11828 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11829 {
11830   si->player_uuid = NULL;       // (will be set later)
11831   si->player_version = 1;       // (will be set later)
11832
11833   si->use_api_server = TRUE;
11834   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11835   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11836   si->ask_for_uploading_tapes = TRUE;
11837   si->ask_for_remaining_tapes = FALSE;
11838   si->provide_uploading_tapes = TRUE;
11839   si->ask_for_using_api_server = TRUE;
11840   si->has_remaining_tapes = FALSE;
11841 }
11842
11843 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11844 {
11845   si->editor_cascade.el_bd              = TRUE;
11846   si->editor_cascade.el_bd_native       = TRUE;
11847   si->editor_cascade.el_bd_effects      = FALSE;
11848   si->editor_cascade.el_em              = TRUE;
11849   si->editor_cascade.el_emc             = TRUE;
11850   si->editor_cascade.el_rnd             = TRUE;
11851   si->editor_cascade.el_sb              = TRUE;
11852   si->editor_cascade.el_sp              = TRUE;
11853   si->editor_cascade.el_dc              = TRUE;
11854   si->editor_cascade.el_dx              = TRUE;
11855
11856   si->editor_cascade.el_mm              = TRUE;
11857   si->editor_cascade.el_df              = TRUE;
11858
11859   si->editor_cascade.el_chars           = FALSE;
11860   si->editor_cascade.el_steel_chars     = FALSE;
11861   si->editor_cascade.el_ce              = FALSE;
11862   si->editor_cascade.el_ge              = FALSE;
11863   si->editor_cascade.el_es              = FALSE;
11864   si->editor_cascade.el_ref             = FALSE;
11865   si->editor_cascade.el_user            = FALSE;
11866   si->editor_cascade.el_dynamic         = FALSE;
11867 }
11868
11869 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11870
11871 static char *getHideSetupToken(void *setup_value)
11872 {
11873   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11874
11875   if (setup_value != NULL)
11876     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11877
11878   return hide_setup_token;
11879 }
11880
11881 void setHideSetupEntry(void *setup_value)
11882 {
11883   char *hide_setup_token = getHideSetupToken(setup_value);
11884
11885   if (hide_setup_hash == NULL)
11886     hide_setup_hash = newSetupFileHash();
11887
11888   if (setup_value != NULL)
11889     setHashEntry(hide_setup_hash, hide_setup_token, "");
11890 }
11891
11892 void removeHideSetupEntry(void *setup_value)
11893 {
11894   char *hide_setup_token = getHideSetupToken(setup_value);
11895
11896   if (setup_value != NULL)
11897     removeHashEntry(hide_setup_hash, hide_setup_token);
11898 }
11899
11900 boolean hideSetupEntry(void *setup_value)
11901 {
11902   char *hide_setup_token = getHideSetupToken(setup_value);
11903
11904   return (setup_value != NULL &&
11905           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11906 }
11907
11908 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11909                                       struct TokenInfo *token_info,
11910                                       int token_nr, char *token_text)
11911 {
11912   char *token_hide_text = getStringCat2(token_text, ".hide");
11913   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11914
11915   // set the value of this setup option in the setup option structure
11916   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11917
11918   // check if this setup option should be hidden in the setup menu
11919   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11920     setHideSetupEntry(token_info[token_nr].value);
11921
11922   free(token_hide_text);
11923 }
11924
11925 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11926                                       struct TokenInfo *token_info,
11927                                       int token_nr)
11928 {
11929   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11930                             token_info[token_nr].text);
11931 }
11932
11933 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11934 {
11935   int i, pnr;
11936
11937   if (!setup_file_hash)
11938     return;
11939
11940   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11941     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11942
11943   setup.touch.grid_initialized = TRUE;
11944   for (i = 0; i < 2; i++)
11945   {
11946     int grid_xsize = setup.touch.grid_xsize[i];
11947     int grid_ysize = setup.touch.grid_ysize[i];
11948     int x, y;
11949
11950     // if virtual buttons are not loaded from setup file, repeat initializing
11951     // virtual buttons grid with default values later when video is initialized
11952     if (grid_xsize == -1 ||
11953         grid_ysize == -1)
11954     {
11955       setup.touch.grid_initialized = FALSE;
11956
11957       continue;
11958     }
11959
11960     for (y = 0; y < grid_ysize; y++)
11961     {
11962       char token_string[MAX_LINE_LEN];
11963
11964       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11965
11966       char *value_string = getHashEntry(setup_file_hash, token_string);
11967
11968       if (value_string == NULL)
11969         continue;
11970
11971       for (x = 0; x < grid_xsize; x++)
11972       {
11973         char c = value_string[x];
11974
11975         setup.touch.grid_button[i][x][y] =
11976           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11977       }
11978     }
11979   }
11980
11981   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11982     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11983
11984   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11985     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11986
11987   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11988   {
11989     char prefix[30];
11990
11991     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11992
11993     setup_input = setup.input[pnr];
11994     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11995     {
11996       char full_token[100];
11997
11998       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11999       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
12000                                 full_token);
12001     }
12002     setup.input[pnr] = setup_input;
12003   }
12004
12005   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12006     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
12007
12008   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
12009     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
12010
12011   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12012     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
12013
12014   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12015     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
12016
12017   setHideRelatedSetupEntries();
12018 }
12019
12020 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
12021 {
12022   int i;
12023
12024   if (!setup_file_hash)
12025     return;
12026
12027   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12028     setSetupInfo(auto_setup_tokens, i,
12029                  getHashEntry(setup_file_hash,
12030                               auto_setup_tokens[i].text));
12031 }
12032
12033 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
12034 {
12035   int i;
12036
12037   if (!setup_file_hash)
12038     return;
12039
12040   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12041     setSetupInfo(server_setup_tokens, i,
12042                  getHashEntry(setup_file_hash,
12043                               server_setup_tokens[i].text));
12044 }
12045
12046 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
12047 {
12048   int i;
12049
12050   if (!setup_file_hash)
12051     return;
12052
12053   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12054     setSetupInfo(editor_cascade_setup_tokens, i,
12055                  getHashEntry(setup_file_hash,
12056                               editor_cascade_setup_tokens[i].text));
12057 }
12058
12059 void LoadUserNames(void)
12060 {
12061   int last_user_nr = user.nr;
12062   int i;
12063
12064   if (global.user_names != NULL)
12065   {
12066     for (i = 0; i < MAX_PLAYER_NAMES; i++)
12067       checked_free(global.user_names[i]);
12068
12069     checked_free(global.user_names);
12070   }
12071
12072   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
12073
12074   for (i = 0; i < MAX_PLAYER_NAMES; i++)
12075   {
12076     user.nr = i;
12077
12078     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
12079
12080     if (setup_file_hash)
12081     {
12082       char *player_name = getHashEntry(setup_file_hash, "player_name");
12083
12084       global.user_names[i] = getFixedUserName(player_name);
12085
12086       freeSetupFileHash(setup_file_hash);
12087     }
12088
12089     if (global.user_names[i] == NULL)
12090       global.user_names[i] = getStringCopy(getDefaultUserName(i));
12091   }
12092
12093   user.nr = last_user_nr;
12094 }
12095
12096 void LoadSetupFromFilename(char *filename)
12097 {
12098   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12099
12100   if (setup_file_hash)
12101   {
12102     decodeSetupFileHash_Default(setup_file_hash);
12103
12104     freeSetupFileHash(setup_file_hash);
12105   }
12106   else
12107   {
12108     Debug("setup", "using default setup values");
12109   }
12110 }
12111
12112 static void LoadSetup_SpecialPostProcessing(void)
12113 {
12114   char *player_name_new;
12115
12116   // needed to work around problems with fixed length strings
12117   player_name_new = getFixedUserName(setup.player_name);
12118   free(setup.player_name);
12119   setup.player_name = player_name_new;
12120
12121   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12122   if (setup.scroll_delay == FALSE)
12123   {
12124     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12125     setup.scroll_delay = TRUE;                  // now always "on"
12126   }
12127
12128   // make sure that scroll delay value stays inside valid range
12129   setup.scroll_delay_value =
12130     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12131 }
12132
12133 void LoadSetup_Default(void)
12134 {
12135   char *filename;
12136
12137   // always start with reliable default values
12138   setSetupInfoToDefaults(&setup);
12139
12140   // try to load setup values from default setup file
12141   filename = getDefaultSetupFilename();
12142
12143   if (fileExists(filename))
12144     LoadSetupFromFilename(filename);
12145
12146   // try to load setup values from platform setup file
12147   filename = getPlatformSetupFilename();
12148
12149   if (fileExists(filename))
12150     LoadSetupFromFilename(filename);
12151
12152   // try to load setup values from user setup file
12153   filename = getSetupFilename();
12154
12155   LoadSetupFromFilename(filename);
12156
12157   LoadSetup_SpecialPostProcessing();
12158 }
12159
12160 void LoadSetup_AutoSetup(void)
12161 {
12162   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12163   SetupFileHash *setup_file_hash = NULL;
12164
12165   // always start with reliable default values
12166   setSetupInfoToDefaults_AutoSetup(&setup);
12167
12168   setup_file_hash = loadSetupFileHash(filename);
12169
12170   if (setup_file_hash)
12171   {
12172     decodeSetupFileHash_AutoSetup(setup_file_hash);
12173
12174     freeSetupFileHash(setup_file_hash);
12175   }
12176
12177   free(filename);
12178 }
12179
12180 void LoadSetup_ServerSetup(void)
12181 {
12182   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12183   SetupFileHash *setup_file_hash = NULL;
12184
12185   // always start with reliable default values
12186   setSetupInfoToDefaults_ServerSetup(&setup);
12187
12188   setup_file_hash = loadSetupFileHash(filename);
12189
12190   if (setup_file_hash)
12191   {
12192     decodeSetupFileHash_ServerSetup(setup_file_hash);
12193
12194     freeSetupFileHash(setup_file_hash);
12195   }
12196
12197   free(filename);
12198
12199   if (setup.player_uuid == NULL)
12200   {
12201     // player UUID does not yet exist in setup file
12202     setup.player_uuid = getStringCopy(getUUID());
12203     setup.player_version = 2;
12204
12205     SaveSetup_ServerSetup();
12206   }
12207 }
12208
12209 void LoadSetup_EditorCascade(void)
12210 {
12211   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12212   SetupFileHash *setup_file_hash = NULL;
12213
12214   // always start with reliable default values
12215   setSetupInfoToDefaults_EditorCascade(&setup);
12216
12217   setup_file_hash = loadSetupFileHash(filename);
12218
12219   if (setup_file_hash)
12220   {
12221     decodeSetupFileHash_EditorCascade(setup_file_hash);
12222
12223     freeSetupFileHash(setup_file_hash);
12224   }
12225
12226   free(filename);
12227 }
12228
12229 void LoadSetup(void)
12230 {
12231   LoadSetup_Default();
12232   LoadSetup_AutoSetup();
12233   LoadSetup_ServerSetup();
12234   LoadSetup_EditorCascade();
12235 }
12236
12237 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12238                                            char *mapping_line)
12239 {
12240   char mapping_guid[MAX_LINE_LEN];
12241   char *mapping_start, *mapping_end;
12242
12243   // get GUID from game controller mapping line: copy complete line
12244   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12245   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12246
12247   // get GUID from game controller mapping line: cut after GUID part
12248   mapping_start = strchr(mapping_guid, ',');
12249   if (mapping_start != NULL)
12250     *mapping_start = '\0';
12251
12252   // cut newline from game controller mapping line
12253   mapping_end = strchr(mapping_line, '\n');
12254   if (mapping_end != NULL)
12255     *mapping_end = '\0';
12256
12257   // add mapping entry to game controller mappings hash
12258   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12259 }
12260
12261 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12262                                                  char *filename)
12263 {
12264   FILE *file;
12265
12266   if (!(file = fopen(filename, MODE_READ)))
12267   {
12268     Warn("cannot read game controller mappings file '%s'", filename);
12269
12270     return;
12271   }
12272
12273   while (!feof(file))
12274   {
12275     char line[MAX_LINE_LEN];
12276
12277     if (!fgets(line, MAX_LINE_LEN, file))
12278       break;
12279
12280     addGameControllerMappingToHash(mappings_hash, line);
12281   }
12282
12283   fclose(file);
12284 }
12285
12286 void SaveSetup_Default(void)
12287 {
12288   char *filename = getSetupFilename();
12289   FILE *file;
12290   int i, pnr;
12291
12292   InitUserDataDirectory();
12293
12294   if (!(file = fopen(filename, MODE_WRITE)))
12295   {
12296     Warn("cannot write setup file '%s'", filename);
12297
12298     return;
12299   }
12300
12301   fprintFileHeader(file, SETUP_FILENAME);
12302
12303   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12304   {
12305     // just to make things nicer :)
12306     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12307         global_setup_tokens[i].value == &setup.sound                    ||
12308         global_setup_tokens[i].value == &setup.graphics_set             ||
12309         global_setup_tokens[i].value == &setup.volume_simple            ||
12310         global_setup_tokens[i].value == &setup.network_mode             ||
12311         global_setup_tokens[i].value == &setup.touch.control_type       ||
12312         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12313         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12314       fprintf(file, "\n");
12315
12316     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12317   }
12318
12319   for (i = 0; i < 2; i++)
12320   {
12321     int grid_xsize = setup.touch.grid_xsize[i];
12322     int grid_ysize = setup.touch.grid_ysize[i];
12323     int x, y;
12324
12325     fprintf(file, "\n");
12326
12327     for (y = 0; y < grid_ysize; y++)
12328     {
12329       char token_string[MAX_LINE_LEN];
12330       char value_string[MAX_LINE_LEN];
12331
12332       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12333
12334       for (x = 0; x < grid_xsize; x++)
12335       {
12336         char c = setup.touch.grid_button[i][x][y];
12337
12338         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12339       }
12340
12341       value_string[grid_xsize] = '\0';
12342
12343       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12344     }
12345   }
12346
12347   fprintf(file, "\n");
12348   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12349     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12350
12351   fprintf(file, "\n");
12352   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12353     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12354
12355   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12356   {
12357     char prefix[30];
12358
12359     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12360     fprintf(file, "\n");
12361
12362     setup_input = setup.input[pnr];
12363     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12364       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12365   }
12366
12367   fprintf(file, "\n");
12368   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12369     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12370
12371   // (internal setup values not saved to user setup file)
12372
12373   fprintf(file, "\n");
12374   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12375     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12376         setup.debug.xsn_mode != AUTO)
12377       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12378
12379   fprintf(file, "\n");
12380   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12381     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12382
12383   fclose(file);
12384
12385   SetFilePermissions(filename, PERMS_PRIVATE);
12386 }
12387
12388 void SaveSetup_AutoSetup(void)
12389 {
12390   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12391   FILE *file;
12392   int i;
12393
12394   InitUserDataDirectory();
12395
12396   if (!(file = fopen(filename, MODE_WRITE)))
12397   {
12398     Warn("cannot write auto setup file '%s'", filename);
12399
12400     free(filename);
12401
12402     return;
12403   }
12404
12405   fprintFileHeader(file, AUTOSETUP_FILENAME);
12406
12407   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12408     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12409
12410   fclose(file);
12411
12412   SetFilePermissions(filename, PERMS_PRIVATE);
12413
12414   free(filename);
12415 }
12416
12417 void SaveSetup_ServerSetup(void)
12418 {
12419   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12420   FILE *file;
12421   int i;
12422
12423   InitUserDataDirectory();
12424
12425   if (!(file = fopen(filename, MODE_WRITE)))
12426   {
12427     Warn("cannot write server setup file '%s'", filename);
12428
12429     free(filename);
12430
12431     return;
12432   }
12433
12434   fprintFileHeader(file, SERVERSETUP_FILENAME);
12435
12436   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12437   {
12438     // just to make things nicer :)
12439     if (server_setup_tokens[i].value == &setup.use_api_server)
12440       fprintf(file, "\n");
12441
12442     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12443   }
12444
12445   fclose(file);
12446
12447   SetFilePermissions(filename, PERMS_PRIVATE);
12448
12449   free(filename);
12450 }
12451
12452 void SaveSetup_EditorCascade(void)
12453 {
12454   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12455   FILE *file;
12456   int i;
12457
12458   InitUserDataDirectory();
12459
12460   if (!(file = fopen(filename, MODE_WRITE)))
12461   {
12462     Warn("cannot write editor cascade state file '%s'", filename);
12463
12464     free(filename);
12465
12466     return;
12467   }
12468
12469   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12470
12471   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12472     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12473
12474   fclose(file);
12475
12476   SetFilePermissions(filename, PERMS_PRIVATE);
12477
12478   free(filename);
12479 }
12480
12481 void SaveSetup(void)
12482 {
12483   SaveSetup_Default();
12484   SaveSetup_AutoSetup();
12485   SaveSetup_ServerSetup();
12486   SaveSetup_EditorCascade();
12487 }
12488
12489 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12490                                                   char *filename)
12491 {
12492   FILE *file;
12493
12494   if (!(file = fopen(filename, MODE_WRITE)))
12495   {
12496     Warn("cannot write game controller mappings file '%s'", filename);
12497
12498     return;
12499   }
12500
12501   BEGIN_HASH_ITERATION(mappings_hash, itr)
12502   {
12503     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12504   }
12505   END_HASH_ITERATION(mappings_hash, itr)
12506
12507   fclose(file);
12508 }
12509
12510 void SaveSetup_AddGameControllerMapping(char *mapping)
12511 {
12512   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12513   SetupFileHash *mappings_hash = newSetupFileHash();
12514
12515   InitUserDataDirectory();
12516
12517   // load existing personal game controller mappings
12518   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12519
12520   // add new mapping to personal game controller mappings
12521   addGameControllerMappingToHash(mappings_hash, mapping);
12522
12523   // save updated personal game controller mappings
12524   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12525
12526   freeSetupFileHash(mappings_hash);
12527   free(filename);
12528 }
12529
12530 void LoadCustomElementDescriptions(void)
12531 {
12532   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12533   SetupFileHash *setup_file_hash;
12534   int i;
12535
12536   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12537   {
12538     if (element_info[i].custom_description != NULL)
12539     {
12540       free(element_info[i].custom_description);
12541       element_info[i].custom_description = NULL;
12542     }
12543   }
12544
12545   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12546     return;
12547
12548   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12549   {
12550     char *token = getStringCat2(element_info[i].token_name, ".name");
12551     char *value = getHashEntry(setup_file_hash, token);
12552
12553     if (value != NULL)
12554       element_info[i].custom_description = getStringCopy(value);
12555
12556     free(token);
12557   }
12558
12559   freeSetupFileHash(setup_file_hash);
12560 }
12561
12562 static int getElementFromToken(char *token)
12563 {
12564   char *value = getHashEntry(element_token_hash, token);
12565
12566   if (value != NULL)
12567     return atoi(value);
12568
12569   Warn("unknown element token '%s'", token);
12570
12571   return EL_UNDEFINED;
12572 }
12573
12574 void FreeGlobalAnimEventInfo(void)
12575 {
12576   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12577
12578   if (gaei->event_list == NULL)
12579     return;
12580
12581   int i;
12582
12583   for (i = 0; i < gaei->num_event_lists; i++)
12584   {
12585     checked_free(gaei->event_list[i]->event_value);
12586     checked_free(gaei->event_list[i]);
12587   }
12588
12589   checked_free(gaei->event_list);
12590
12591   gaei->event_list = NULL;
12592   gaei->num_event_lists = 0;
12593 }
12594
12595 static int AddGlobalAnimEventList(void)
12596 {
12597   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12598   int list_pos = gaei->num_event_lists++;
12599
12600   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12601                                      sizeof(struct GlobalAnimEventListInfo *));
12602
12603   gaei->event_list[list_pos] =
12604     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12605
12606   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12607
12608   gaeli->event_value = NULL;
12609   gaeli->num_event_values = 0;
12610
12611   return list_pos;
12612 }
12613
12614 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12615 {
12616   // do not add empty global animation events
12617   if (event_value == ANIM_EVENT_NONE)
12618     return list_pos;
12619
12620   // if list position is undefined, create new list
12621   if (list_pos == ANIM_EVENT_UNDEFINED)
12622     list_pos = AddGlobalAnimEventList();
12623
12624   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12625   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12626   int value_pos = gaeli->num_event_values++;
12627
12628   gaeli->event_value = checked_realloc(gaeli->event_value,
12629                                        gaeli->num_event_values * sizeof(int *));
12630
12631   gaeli->event_value[value_pos] = event_value;
12632
12633   return list_pos;
12634 }
12635
12636 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12637 {
12638   if (list_pos == ANIM_EVENT_UNDEFINED)
12639     return ANIM_EVENT_NONE;
12640
12641   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12642   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12643
12644   return gaeli->event_value[value_pos];
12645 }
12646
12647 int GetGlobalAnimEventValueCount(int list_pos)
12648 {
12649   if (list_pos == ANIM_EVENT_UNDEFINED)
12650     return 0;
12651
12652   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12653   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12654
12655   return gaeli->num_event_values;
12656 }
12657
12658 // This function checks if a string <s> of the format "string1, string2, ..."
12659 // exactly contains a string <s_contained>.
12660
12661 static boolean string_has_parameter(char *s, char *s_contained)
12662 {
12663   char *substring;
12664
12665   if (s == NULL || s_contained == NULL)
12666     return FALSE;
12667
12668   if (strlen(s_contained) > strlen(s))
12669     return FALSE;
12670
12671   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12672   {
12673     char next_char = s[strlen(s_contained)];
12674
12675     // check if next character is delimiter or whitespace
12676     if (next_char == ',' || next_char == '\0' ||
12677         next_char == ' ' || next_char == '\t')
12678       return TRUE;
12679   }
12680
12681   // check if string contains another parameter string after a comma
12682   substring = strchr(s, ',');
12683   if (substring == NULL)        // string does not contain a comma
12684     return FALSE;
12685
12686   // advance string pointer to next character after the comma
12687   substring++;
12688
12689   // skip potential whitespaces after the comma
12690   while (*substring == ' ' || *substring == '\t')
12691     substring++;
12692
12693   return string_has_parameter(substring, s_contained);
12694 }
12695
12696 static int get_anim_parameter_value_ce(char *s)
12697 {
12698   char *s_ptr = s;
12699   char *pattern_1 = "ce_change:custom_";
12700   char *pattern_2 = ".page_";
12701   int pattern_1_len = strlen(pattern_1);
12702   char *matching_char = strstr(s_ptr, pattern_1);
12703   int result = ANIM_EVENT_NONE;
12704
12705   if (matching_char == NULL)
12706     return ANIM_EVENT_NONE;
12707
12708   result = ANIM_EVENT_CE_CHANGE;
12709
12710   s_ptr = matching_char + pattern_1_len;
12711
12712   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12713   if (*s_ptr >= '0' && *s_ptr <= '9')
12714   {
12715     int gic_ce_nr = (*s_ptr++ - '0');
12716
12717     if (*s_ptr >= '0' && *s_ptr <= '9')
12718     {
12719       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12720
12721       if (*s_ptr >= '0' && *s_ptr <= '9')
12722         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12723     }
12724
12725     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12726       return ANIM_EVENT_NONE;
12727
12728     // custom element stored as 0 to 255
12729     gic_ce_nr--;
12730
12731     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12732   }
12733   else
12734   {
12735     // invalid custom element number specified
12736
12737     return ANIM_EVENT_NONE;
12738   }
12739
12740   // check for change page number ("page_X" or "page_XX") (optional)
12741   if (strPrefix(s_ptr, pattern_2))
12742   {
12743     s_ptr += strlen(pattern_2);
12744
12745     if (*s_ptr >= '0' && *s_ptr <= '9')
12746     {
12747       int gic_page_nr = (*s_ptr++ - '0');
12748
12749       if (*s_ptr >= '0' && *s_ptr <= '9')
12750         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12751
12752       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12753         return ANIM_EVENT_NONE;
12754
12755       // change page stored as 1 to 32 (0 means "all change pages")
12756
12757       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12758     }
12759     else
12760     {
12761       // invalid animation part number specified
12762
12763       return ANIM_EVENT_NONE;
12764     }
12765   }
12766
12767   // discard result if next character is neither delimiter nor whitespace
12768   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12769         *s_ptr == ' ' || *s_ptr == '\t'))
12770     return ANIM_EVENT_NONE;
12771
12772   return result;
12773 }
12774
12775 static int get_anim_parameter_value(char *s)
12776 {
12777   int event_value[] =
12778   {
12779     ANIM_EVENT_CLICK,
12780     ANIM_EVENT_INIT,
12781     ANIM_EVENT_START,
12782     ANIM_EVENT_END,
12783     ANIM_EVENT_POST
12784   };
12785   char *pattern_1[] =
12786   {
12787     "click:anim_",
12788     "init:anim_",
12789     "start:anim_",
12790     "end:anim_",
12791     "post:anim_"
12792   };
12793   char *pattern_2 = ".part_";
12794   char *matching_char = NULL;
12795   char *s_ptr = s;
12796   int pattern_1_len = 0;
12797   int result = ANIM_EVENT_NONE;
12798   int i;
12799
12800   result = get_anim_parameter_value_ce(s);
12801
12802   if (result != ANIM_EVENT_NONE)
12803     return result;
12804
12805   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12806   {
12807     matching_char = strstr(s_ptr, pattern_1[i]);
12808     pattern_1_len = strlen(pattern_1[i]);
12809     result = event_value[i];
12810
12811     if (matching_char != NULL)
12812       break;
12813   }
12814
12815   if (matching_char == NULL)
12816     return ANIM_EVENT_NONE;
12817
12818   s_ptr = matching_char + pattern_1_len;
12819
12820   // check for main animation number ("anim_X" or "anim_XX")
12821   if (*s_ptr >= '0' && *s_ptr <= '9')
12822   {
12823     int gic_anim_nr = (*s_ptr++ - '0');
12824
12825     if (*s_ptr >= '0' && *s_ptr <= '9')
12826       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12827
12828     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12829       return ANIM_EVENT_NONE;
12830
12831     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12832   }
12833   else
12834   {
12835     // invalid main animation number specified
12836
12837     return ANIM_EVENT_NONE;
12838   }
12839
12840   // check for animation part number ("part_X" or "part_XX") (optional)
12841   if (strPrefix(s_ptr, pattern_2))
12842   {
12843     s_ptr += strlen(pattern_2);
12844
12845     if (*s_ptr >= '0' && *s_ptr <= '9')
12846     {
12847       int gic_part_nr = (*s_ptr++ - '0');
12848
12849       if (*s_ptr >= '0' && *s_ptr <= '9')
12850         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12851
12852       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12853         return ANIM_EVENT_NONE;
12854
12855       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12856     }
12857     else
12858     {
12859       // invalid animation part number specified
12860
12861       return ANIM_EVENT_NONE;
12862     }
12863   }
12864
12865   // discard result if next character is neither delimiter nor whitespace
12866   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12867         *s_ptr == ' ' || *s_ptr == '\t'))
12868     return ANIM_EVENT_NONE;
12869
12870   return result;
12871 }
12872
12873 static int get_anim_parameter_values(char *s)
12874 {
12875   int list_pos = ANIM_EVENT_UNDEFINED;
12876   int event_value = ANIM_EVENT_DEFAULT;
12877
12878   if (string_has_parameter(s, "any"))
12879     event_value |= ANIM_EVENT_ANY;
12880
12881   if (string_has_parameter(s, "click:self") ||
12882       string_has_parameter(s, "click") ||
12883       string_has_parameter(s, "self"))
12884     event_value |= ANIM_EVENT_SELF;
12885
12886   if (string_has_parameter(s, "unclick:any"))
12887     event_value |= ANIM_EVENT_UNCLICK_ANY;
12888
12889   // if animation event found, add it to global animation event list
12890   if (event_value != ANIM_EVENT_NONE)
12891     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12892
12893   while (s != NULL)
12894   {
12895     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12896     event_value = get_anim_parameter_value(s);
12897
12898     // if animation event found, add it to global animation event list
12899     if (event_value != ANIM_EVENT_NONE)
12900       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12901
12902     // continue with next part of the string, starting with next comma
12903     s = strchr(s + 1, ',');
12904   }
12905
12906   return list_pos;
12907 }
12908
12909 static int get_anim_action_parameter_value(char *token)
12910 {
12911   // check most common default case first to massively speed things up
12912   if (strEqual(token, ARG_UNDEFINED))
12913     return ANIM_EVENT_ACTION_NONE;
12914
12915   int result = getImageIDFromToken(token);
12916
12917   if (result == -1)
12918   {
12919     char *gfx_token = getStringCat2("gfx.", token);
12920
12921     result = getImageIDFromToken(gfx_token);
12922
12923     checked_free(gfx_token);
12924   }
12925
12926   if (result == -1)
12927   {
12928     Key key = getKeyFromX11KeyName(token);
12929
12930     if (key != KSYM_UNDEFINED)
12931       result = -(int)key;
12932   }
12933
12934   if (result == -1)
12935   {
12936     if (isURL(token))
12937     {
12938       result = get_hash_from_string(token);     // unsigned int => int
12939       result = ABS(result);                     // may be negative now
12940       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12941
12942       setHashEntry(anim_url_hash, int2str(result, 0), token);
12943     }
12944   }
12945
12946   if (result == -1)
12947     result = ANIM_EVENT_ACTION_NONE;
12948
12949   return result;
12950 }
12951
12952 int get_parameter_value(char *value_raw, char *suffix, int type)
12953 {
12954   char *value = getStringToLower(value_raw);
12955   int result = 0;       // probably a save default value
12956
12957   if (strEqual(suffix, ".direction"))
12958   {
12959     result = (strEqual(value, "left")  ? MV_LEFT :
12960               strEqual(value, "right") ? MV_RIGHT :
12961               strEqual(value, "up")    ? MV_UP :
12962               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12963   }
12964   else if (strEqual(suffix, ".position"))
12965   {
12966     result = (strEqual(value, "left")   ? POS_LEFT :
12967               strEqual(value, "right")  ? POS_RIGHT :
12968               strEqual(value, "top")    ? POS_TOP :
12969               strEqual(value, "upper")  ? POS_UPPER :
12970               strEqual(value, "middle") ? POS_MIDDLE :
12971               strEqual(value, "lower")  ? POS_LOWER :
12972               strEqual(value, "bottom") ? POS_BOTTOM :
12973               strEqual(value, "any")    ? POS_ANY :
12974               strEqual(value, "ce")     ? POS_CE :
12975               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12976               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12977   }
12978   else if (strEqual(suffix, ".align"))
12979   {
12980     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12981               strEqual(value, "right")  ? ALIGN_RIGHT :
12982               strEqual(value, "center") ? ALIGN_CENTER :
12983               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12984   }
12985   else if (strEqual(suffix, ".valign"))
12986   {
12987     result = (strEqual(value, "top")    ? VALIGN_TOP :
12988               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12989               strEqual(value, "middle") ? VALIGN_MIDDLE :
12990               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12991   }
12992   else if (strEqual(suffix, ".anim_mode"))
12993   {
12994     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12995               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12996               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12997               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12998               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12999               string_has_parameter(value, "random")     ? ANIM_RANDOM :
13000               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
13001               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
13002               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
13003               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
13004               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
13005               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
13006               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
13007               string_has_parameter(value, "all")        ? ANIM_ALL :
13008               string_has_parameter(value, "tiled")      ? ANIM_TILED :
13009               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
13010               ANIM_DEFAULT);
13011
13012     if (string_has_parameter(value, "once"))
13013       result |= ANIM_ONCE;
13014
13015     if (string_has_parameter(value, "reverse"))
13016       result |= ANIM_REVERSE;
13017
13018     if (string_has_parameter(value, "opaque_player"))
13019       result |= ANIM_OPAQUE_PLAYER;
13020
13021     if (string_has_parameter(value, "static_panel"))
13022       result |= ANIM_STATIC_PANEL;
13023   }
13024   else if (strEqual(suffix, ".init_event") ||
13025            strEqual(suffix, ".anim_event"))
13026   {
13027     result = get_anim_parameter_values(value);
13028   }
13029   else if (strEqual(suffix, ".init_delay_action") ||
13030            strEqual(suffix, ".anim_delay_action") ||
13031            strEqual(suffix, ".post_delay_action") ||
13032            strEqual(suffix, ".init_event_action") ||
13033            strEqual(suffix, ".anim_event_action"))
13034   {
13035     result = get_anim_action_parameter_value(value_raw);
13036   }
13037   else if (strEqual(suffix, ".class"))
13038   {
13039     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13040               get_hash_from_string(value));
13041   }
13042   else if (strEqual(suffix, ".style"))
13043   {
13044     result = STYLE_DEFAULT;
13045
13046     if (string_has_parameter(value, "accurate_borders"))
13047       result |= STYLE_ACCURATE_BORDERS;
13048
13049     if (string_has_parameter(value, "inner_corners"))
13050       result |= STYLE_INNER_CORNERS;
13051
13052     if (string_has_parameter(value, "reverse"))
13053       result |= STYLE_REVERSE;
13054
13055     if (string_has_parameter(value, "leftmost_position"))
13056       result |= STYLE_LEFTMOST_POSITION;
13057
13058     if (string_has_parameter(value, "block_clicks"))
13059       result |= STYLE_BLOCK;
13060
13061     if (string_has_parameter(value, "passthrough_clicks"))
13062       result |= STYLE_PASSTHROUGH;
13063
13064     if (string_has_parameter(value, "multiple_actions"))
13065       result |= STYLE_MULTIPLE_ACTIONS;
13066
13067     if (string_has_parameter(value, "consume_ce_event"))
13068       result |= STYLE_CONSUME_CE_EVENT;
13069   }
13070   else if (strEqual(suffix, ".fade_mode"))
13071   {
13072     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
13073               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
13074               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
13075               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
13076               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
13077               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
13078               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
13079               FADE_MODE_DEFAULT);
13080   }
13081   else if (strEqual(suffix, ".auto_delay_unit"))
13082   {
13083     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
13084               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
13085               AUTO_DELAY_UNIT_DEFAULT);
13086   }
13087   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
13088   {
13089     result = gfx.get_font_from_token_function(value);
13090   }
13091   else          // generic parameter of type integer or boolean
13092   {
13093     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
13094               type == TYPE_INTEGER ? get_integer_from_string(value) :
13095               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
13096               ARG_UNDEFINED_VALUE);
13097   }
13098
13099   free(value);
13100
13101   return result;
13102 }
13103
13104 static int get_token_parameter_value(char *token, char *value_raw)
13105 {
13106   char *suffix;
13107
13108   if (token == NULL || value_raw == NULL)
13109     return ARG_UNDEFINED_VALUE;
13110
13111   suffix = strrchr(token, '.');
13112   if (suffix == NULL)
13113     suffix = token;
13114
13115   if (strEqual(suffix, ".element"))
13116     return getElementFromToken(value_raw);
13117
13118   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13119   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13120 }
13121
13122 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13123                                      boolean ignore_defaults)
13124 {
13125   int i;
13126
13127   for (i = 0; image_config_vars[i].token != NULL; i++)
13128   {
13129     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13130
13131     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13132     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13133       continue;
13134
13135     if (value != NULL)
13136       *image_config_vars[i].value =
13137         get_token_parameter_value(image_config_vars[i].token, value);
13138   }
13139 }
13140
13141 void InitMenuDesignSettings_Static(void)
13142 {
13143   // always start with reliable default values from static default config
13144   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13145 }
13146
13147 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13148 {
13149   int i;
13150
13151   // the following initializes hierarchical values from static configuration
13152
13153   // special case: initialize "ARG_DEFAULT" values in static default config
13154   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13155   titlescreen_initial_first_default.fade_mode  =
13156     title_initial_first_default.fade_mode;
13157   titlescreen_initial_first_default.fade_delay =
13158     title_initial_first_default.fade_delay;
13159   titlescreen_initial_first_default.post_delay =
13160     title_initial_first_default.post_delay;
13161   titlescreen_initial_first_default.auto_delay =
13162     title_initial_first_default.auto_delay;
13163   titlescreen_initial_first_default.auto_delay_unit =
13164     title_initial_first_default.auto_delay_unit;
13165   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13166   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13167   titlescreen_first_default.post_delay = title_first_default.post_delay;
13168   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13169   titlescreen_first_default.auto_delay_unit =
13170     title_first_default.auto_delay_unit;
13171   titlemessage_initial_first_default.fade_mode  =
13172     title_initial_first_default.fade_mode;
13173   titlemessage_initial_first_default.fade_delay =
13174     title_initial_first_default.fade_delay;
13175   titlemessage_initial_first_default.post_delay =
13176     title_initial_first_default.post_delay;
13177   titlemessage_initial_first_default.auto_delay =
13178     title_initial_first_default.auto_delay;
13179   titlemessage_initial_first_default.auto_delay_unit =
13180     title_initial_first_default.auto_delay_unit;
13181   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13182   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13183   titlemessage_first_default.post_delay = title_first_default.post_delay;
13184   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13185   titlemessage_first_default.auto_delay_unit =
13186     title_first_default.auto_delay_unit;
13187
13188   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13189   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13190   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13191   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13192   titlescreen_initial_default.auto_delay_unit =
13193     title_initial_default.auto_delay_unit;
13194   titlescreen_default.fade_mode  = title_default.fade_mode;
13195   titlescreen_default.fade_delay = title_default.fade_delay;
13196   titlescreen_default.post_delay = title_default.post_delay;
13197   titlescreen_default.auto_delay = title_default.auto_delay;
13198   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13199   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13200   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13201   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13202   titlemessage_initial_default.auto_delay_unit =
13203     title_initial_default.auto_delay_unit;
13204   titlemessage_default.fade_mode  = title_default.fade_mode;
13205   titlemessage_default.fade_delay = title_default.fade_delay;
13206   titlemessage_default.post_delay = title_default.post_delay;
13207   titlemessage_default.auto_delay = title_default.auto_delay;
13208   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13209
13210   // special case: initialize "ARG_DEFAULT" values in static default config
13211   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13212   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13213   {
13214     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13215     titlescreen_first[i] = titlescreen_first_default;
13216     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13217     titlemessage_first[i] = titlemessage_first_default;
13218
13219     titlescreen_initial[i] = titlescreen_initial_default;
13220     titlescreen[i] = titlescreen_default;
13221     titlemessage_initial[i] = titlemessage_initial_default;
13222     titlemessage[i] = titlemessage_default;
13223   }
13224
13225   // special case: initialize "ARG_DEFAULT" values in static default config
13226   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13227   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13228   {
13229     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13230       continue;
13231
13232     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13233     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13234     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13235   }
13236
13237   // special case: initialize "ARG_DEFAULT" values in static default config
13238   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13239   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13240   {
13241     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13242     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13243     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13244
13245     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13246       continue;
13247
13248     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13249   }
13250 }
13251
13252 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13253 {
13254   static struct
13255   {
13256     struct XY *dst, *src;
13257   }
13258   game_buttons_xy[] =
13259   {
13260     { &game.button.save,        &game.button.stop       },
13261     { &game.button.pause2,      &game.button.pause      },
13262     { &game.button.load,        &game.button.play       },
13263     { &game.button.undo,        &game.button.stop       },
13264     { &game.button.redo,        &game.button.play       },
13265
13266     { NULL,                     NULL                    }
13267   };
13268   int i, j;
13269
13270   // special case: initialize later added SETUP list size from LEVELS value
13271   if (menu.list_size[GAME_MODE_SETUP] == -1)
13272     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13273
13274   // set default position for snapshot buttons to stop/pause/play buttons
13275   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13276     if ((*game_buttons_xy[i].dst).x == -1 &&
13277         (*game_buttons_xy[i].dst).y == -1)
13278       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13279
13280   // --------------------------------------------------------------------------
13281   // dynamic viewports (including playfield margins, borders and alignments)
13282   // --------------------------------------------------------------------------
13283
13284   // dynamic viewports currently only supported for landscape mode
13285   int display_width  = MAX(video.display_width, video.display_height);
13286   int display_height = MIN(video.display_width, video.display_height);
13287
13288   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13289   {
13290     struct RectWithBorder *vp_window    = &viewport.window[i];
13291     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13292     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13293     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13294     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13295     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13296     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13297     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13298
13299     // adjust window size if min/max width/height is specified
13300
13301     if (vp_window->min_width != -1)
13302     {
13303       int window_width = display_width;
13304
13305       // when using static window height, use aspect ratio of display
13306       if (vp_window->min_height == -1)
13307         window_width = vp_window->height * display_width / display_height;
13308
13309       vp_window->width = MAX(vp_window->min_width, window_width);
13310     }
13311
13312     if (vp_window->min_height != -1)
13313     {
13314       int window_height = display_height;
13315
13316       // when using static window width, use aspect ratio of display
13317       if (vp_window->min_width == -1)
13318         window_height = vp_window->width * display_height / display_width;
13319
13320       vp_window->height = MAX(vp_window->min_height, window_height);
13321     }
13322
13323     if (vp_window->max_width != -1)
13324       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13325
13326     if (vp_window->max_height != -1)
13327       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13328
13329     int playfield_width  = vp_window->width;
13330     int playfield_height = vp_window->height;
13331
13332     // adjust playfield size and position according to specified margins
13333
13334     playfield_width  -= vp_playfield->margin_left;
13335     playfield_width  -= vp_playfield->margin_right;
13336
13337     playfield_height -= vp_playfield->margin_top;
13338     playfield_height -= vp_playfield->margin_bottom;
13339
13340     // adjust playfield size if min/max width/height is specified
13341
13342     if (vp_playfield->min_width != -1)
13343       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13344
13345     if (vp_playfield->min_height != -1)
13346       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13347
13348     if (vp_playfield->max_width != -1)
13349       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13350
13351     if (vp_playfield->max_height != -1)
13352       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13353
13354     // adjust playfield position according to specified alignment
13355
13356     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13357       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13358     else if (vp_playfield->align == ALIGN_CENTER)
13359       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13360     else if (vp_playfield->align == ALIGN_RIGHT)
13361       vp_playfield->x += playfield_width - vp_playfield->width;
13362
13363     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13364       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13365     else if (vp_playfield->valign == VALIGN_MIDDLE)
13366       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13367     else if (vp_playfield->valign == VALIGN_BOTTOM)
13368       vp_playfield->y += playfield_height - vp_playfield->height;
13369
13370     vp_playfield->x += vp_playfield->margin_left;
13371     vp_playfield->y += vp_playfield->margin_top;
13372
13373     // adjust individual playfield borders if only default border is specified
13374
13375     if (vp_playfield->border_left == -1)
13376       vp_playfield->border_left = vp_playfield->border_size;
13377     if (vp_playfield->border_right == -1)
13378       vp_playfield->border_right = vp_playfield->border_size;
13379     if (vp_playfield->border_top == -1)
13380       vp_playfield->border_top = vp_playfield->border_size;
13381     if (vp_playfield->border_bottom == -1)
13382       vp_playfield->border_bottom = vp_playfield->border_size;
13383
13384     // set dynamic playfield borders if borders are specified as undefined
13385     // (but only if window size was dynamic and playfield size was static)
13386
13387     if (dynamic_window_width && !dynamic_playfield_width)
13388     {
13389       if (vp_playfield->border_left == -1)
13390       {
13391         vp_playfield->border_left = (vp_playfield->x -
13392                                      vp_playfield->margin_left);
13393         vp_playfield->x     -= vp_playfield->border_left;
13394         vp_playfield->width += vp_playfield->border_left;
13395       }
13396
13397       if (vp_playfield->border_right == -1)
13398       {
13399         vp_playfield->border_right = (vp_window->width -
13400                                       vp_playfield->x -
13401                                       vp_playfield->width -
13402                                       vp_playfield->margin_right);
13403         vp_playfield->width += vp_playfield->border_right;
13404       }
13405     }
13406
13407     if (dynamic_window_height && !dynamic_playfield_height)
13408     {
13409       if (vp_playfield->border_top == -1)
13410       {
13411         vp_playfield->border_top = (vp_playfield->y -
13412                                     vp_playfield->margin_top);
13413         vp_playfield->y      -= vp_playfield->border_top;
13414         vp_playfield->height += vp_playfield->border_top;
13415       }
13416
13417       if (vp_playfield->border_bottom == -1)
13418       {
13419         vp_playfield->border_bottom = (vp_window->height -
13420                                        vp_playfield->y -
13421                                        vp_playfield->height -
13422                                        vp_playfield->margin_bottom);
13423         vp_playfield->height += vp_playfield->border_bottom;
13424       }
13425     }
13426
13427     // adjust playfield size to be a multiple of a defined alignment tile size
13428
13429     int align_size = vp_playfield->align_size;
13430     int playfield_xtiles = vp_playfield->width  / align_size;
13431     int playfield_ytiles = vp_playfield->height / align_size;
13432     int playfield_width_corrected  = playfield_xtiles * align_size;
13433     int playfield_height_corrected = playfield_ytiles * align_size;
13434     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13435                                  i == GFX_SPECIAL_ARG_EDITOR);
13436
13437     if (is_playfield_mode &&
13438         dynamic_playfield_width &&
13439         vp_playfield->width != playfield_width_corrected)
13440     {
13441       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13442
13443       vp_playfield->width = playfield_width_corrected;
13444
13445       if (vp_playfield->align == ALIGN_LEFT)
13446       {
13447         vp_playfield->border_left += playfield_xdiff;
13448       }
13449       else if (vp_playfield->align == ALIGN_RIGHT)
13450       {
13451         vp_playfield->border_right += playfield_xdiff;
13452       }
13453       else if (vp_playfield->align == ALIGN_CENTER)
13454       {
13455         int border_left_diff  = playfield_xdiff / 2;
13456         int border_right_diff = playfield_xdiff - border_left_diff;
13457
13458         vp_playfield->border_left  += border_left_diff;
13459         vp_playfield->border_right += border_right_diff;
13460       }
13461     }
13462
13463     if (is_playfield_mode &&
13464         dynamic_playfield_height &&
13465         vp_playfield->height != playfield_height_corrected)
13466     {
13467       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13468
13469       vp_playfield->height = playfield_height_corrected;
13470
13471       if (vp_playfield->valign == VALIGN_TOP)
13472       {
13473         vp_playfield->border_top += playfield_ydiff;
13474       }
13475       else if (vp_playfield->align == VALIGN_BOTTOM)
13476       {
13477         vp_playfield->border_right += playfield_ydiff;
13478       }
13479       else if (vp_playfield->align == VALIGN_MIDDLE)
13480       {
13481         int border_top_diff    = playfield_ydiff / 2;
13482         int border_bottom_diff = playfield_ydiff - border_top_diff;
13483
13484         vp_playfield->border_top    += border_top_diff;
13485         vp_playfield->border_bottom += border_bottom_diff;
13486       }
13487     }
13488
13489     // adjust door positions according to specified alignment
13490
13491     for (j = 0; j < 2; j++)
13492     {
13493       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13494
13495       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13496         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13497       else if (vp_door->align == ALIGN_CENTER)
13498         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13499       else if (vp_door->align == ALIGN_RIGHT)
13500         vp_door->x += vp_window->width - vp_door->width;
13501
13502       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13503         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13504       else if (vp_door->valign == VALIGN_MIDDLE)
13505         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13506       else if (vp_door->valign == VALIGN_BOTTOM)
13507         vp_door->y += vp_window->height - vp_door->height;
13508     }
13509   }
13510 }
13511
13512 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13513 {
13514   static struct
13515   {
13516     struct XYTileSize *dst, *src;
13517     int graphic;
13518   }
13519   editor_buttons_xy[] =
13520   {
13521     {
13522       &editor.button.element_left,      &editor.palette.element_left,
13523       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13524     },
13525     {
13526       &editor.button.element_middle,    &editor.palette.element_middle,
13527       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13528     },
13529     {
13530       &editor.button.element_right,     &editor.palette.element_right,
13531       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13532     },
13533
13534     { NULL,                     NULL                    }
13535   };
13536   int i;
13537
13538   // set default position for element buttons to element graphics
13539   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13540   {
13541     if ((*editor_buttons_xy[i].dst).x == -1 &&
13542         (*editor_buttons_xy[i].dst).y == -1)
13543     {
13544       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13545
13546       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13547
13548       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13549     }
13550   }
13551
13552   // adjust editor palette rows and columns if specified to be dynamic
13553
13554   if (editor.palette.cols == -1)
13555   {
13556     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13557     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13558     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13559
13560     editor.palette.cols = (vp_width - sc_width) / bt_width;
13561
13562     if (editor.palette.x == -1)
13563     {
13564       int palette_width = editor.palette.cols * bt_width + sc_width;
13565
13566       editor.palette.x = (vp_width - palette_width) / 2;
13567     }
13568   }
13569
13570   if (editor.palette.rows == -1)
13571   {
13572     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13573     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13574     int tx_height = getFontHeight(FONT_TEXT_2);
13575
13576     editor.palette.rows = (vp_height - tx_height) / bt_height;
13577
13578     if (editor.palette.y == -1)
13579     {
13580       int palette_height = editor.palette.rows * bt_height + tx_height;
13581
13582       editor.palette.y = (vp_height - palette_height) / 2;
13583     }
13584   }
13585 }
13586
13587 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13588                                                       boolean initialize)
13589 {
13590   // special case: check if network and preview player positions are redefined,
13591   // to compare this later against the main menu level preview being redefined
13592   struct TokenIntPtrInfo menu_config_players[] =
13593   {
13594     { "main.network_players.x", &menu.main.network_players.redefined    },
13595     { "main.network_players.y", &menu.main.network_players.redefined    },
13596     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13597     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13598     { "preview.x",              &preview.redefined                      },
13599     { "preview.y",              &preview.redefined                      }
13600   };
13601   int i;
13602
13603   if (initialize)
13604   {
13605     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13606       *menu_config_players[i].value = FALSE;
13607   }
13608   else
13609   {
13610     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13611       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13612         *menu_config_players[i].value = TRUE;
13613   }
13614 }
13615
13616 static void InitMenuDesignSettings_PreviewPlayers(void)
13617 {
13618   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13619 }
13620
13621 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13622 {
13623   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13624 }
13625
13626 static void LoadMenuDesignSettingsFromFilename(char *filename)
13627 {
13628   static struct TitleFadingInfo tfi;
13629   static struct TitleMessageInfo tmi;
13630   static struct TokenInfo title_tokens[] =
13631   {
13632     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13633     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13634     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13635     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13636     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13637
13638     { -1,               NULL,                   NULL                    }
13639   };
13640   static struct TokenInfo titlemessage_tokens[] =
13641   {
13642     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13643     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13644     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13645     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13646     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13647     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13648     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13649     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13650     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13651     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13652     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13653     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13654     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13655     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13656     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13657     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13658     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13659     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13660
13661     { -1,               NULL,                   NULL                    }
13662   };
13663   static struct
13664   {
13665     struct TitleFadingInfo *info;
13666     char *text;
13667   }
13668   title_info[] =
13669   {
13670     // initialize first titles from "enter screen" definitions, if defined
13671     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13672     { &title_first_default,             "menu.enter_screen.TITLE"       },
13673
13674     // initialize title screens from "next screen" definitions, if defined
13675     { &title_initial_default,           "menu.next_screen.TITLE"        },
13676     { &title_default,                   "menu.next_screen.TITLE"        },
13677
13678     { NULL,                             NULL                            }
13679   };
13680   static struct
13681   {
13682     struct TitleMessageInfo *array;
13683     char *text;
13684   }
13685   titlemessage_arrays[] =
13686   {
13687     // initialize first titles from "enter screen" definitions, if defined
13688     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13689     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13690     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13691     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13692
13693     // initialize titles from "next screen" definitions, if defined
13694     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13695     { titlescreen,                      "menu.next_screen.TITLE"        },
13696     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13697     { titlemessage,                     "menu.next_screen.TITLE"        },
13698
13699     // overwrite titles with title definitions, if defined
13700     { titlescreen_initial_first,        "[title_initial]"               },
13701     { titlescreen_first,                "[title]"                       },
13702     { titlemessage_initial_first,       "[title_initial]"               },
13703     { titlemessage_first,               "[title]"                       },
13704
13705     { titlescreen_initial,              "[title_initial]"               },
13706     { titlescreen,                      "[title]"                       },
13707     { titlemessage_initial,             "[title_initial]"               },
13708     { titlemessage,                     "[title]"                       },
13709
13710     // overwrite titles with title screen/message definitions, if defined
13711     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13712     { titlescreen_first,                "[titlescreen]"                 },
13713     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13714     { titlemessage_first,               "[titlemessage]"                },
13715
13716     { titlescreen_initial,              "[titlescreen_initial]"         },
13717     { titlescreen,                      "[titlescreen]"                 },
13718     { titlemessage_initial,             "[titlemessage_initial]"        },
13719     { titlemessage,                     "[titlemessage]"                },
13720
13721     { NULL,                             NULL                            }
13722   };
13723   SetupFileHash *setup_file_hash;
13724   int i, j, k;
13725
13726   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13727     return;
13728
13729   // the following initializes hierarchical values from dynamic configuration
13730
13731   // special case: initialize with default values that may be overwritten
13732   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13733   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13734   {
13735     struct TokenIntPtrInfo menu_config[] =
13736     {
13737       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13738       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13739       { "menu.list_size",       &menu.list_size[i]      }
13740     };
13741
13742     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13743     {
13744       char *token = menu_config[j].token;
13745       char *value = getHashEntry(setup_file_hash, token);
13746
13747       if (value != NULL)
13748         *menu_config[j].value = get_integer_from_string(value);
13749     }
13750   }
13751
13752   // special case: initialize with default values that may be overwritten
13753   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13754   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13755   {
13756     struct TokenIntPtrInfo menu_config[] =
13757     {
13758       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13759       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13760       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13761       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13762       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13763     };
13764
13765     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13766     {
13767       char *token = menu_config[j].token;
13768       char *value = getHashEntry(setup_file_hash, token);
13769
13770       if (value != NULL)
13771         *menu_config[j].value = get_integer_from_string(value);
13772     }
13773   }
13774
13775   // special case: initialize with default values that may be overwritten
13776   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13777   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13778   {
13779     struct TokenIntPtrInfo menu_config[] =
13780     {
13781       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13782       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13783     };
13784
13785     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13786     {
13787       char *token = menu_config[j].token;
13788       char *value = getHashEntry(setup_file_hash, token);
13789
13790       if (value != NULL)
13791         *menu_config[j].value = get_integer_from_string(value);
13792     }
13793   }
13794
13795   // special case: initialize with default values that may be overwritten
13796   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13797   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13798   {
13799     struct TokenIntPtrInfo menu_config[] =
13800     {
13801       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13802       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13803       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13804       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13805       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13806       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13807       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13808       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13809       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13810       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13811     };
13812
13813     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13814     {
13815       char *token = menu_config[j].token;
13816       char *value = getHashEntry(setup_file_hash, token);
13817
13818       if (value != NULL)
13819         *menu_config[j].value = get_integer_from_string(value);
13820     }
13821   }
13822
13823   // special case: initialize with default values that may be overwritten
13824   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13825   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13826   {
13827     struct TokenIntPtrInfo menu_config[] =
13828     {
13829       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13830       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13831       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13832       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13833       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13834       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13835       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13836       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13837       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13838     };
13839
13840     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13841     {
13842       char *token = menu_config[j].token;
13843       char *value = getHashEntry(setup_file_hash, token);
13844
13845       if (value != NULL)
13846         *menu_config[j].value = get_token_parameter_value(token, value);
13847     }
13848   }
13849
13850   // special case: initialize with default values that may be overwritten
13851   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13852   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13853   {
13854     struct
13855     {
13856       char *token_prefix;
13857       struct RectWithBorder *struct_ptr;
13858     }
13859     vp_struct[] =
13860     {
13861       { "viewport.window",      &viewport.window[i]     },
13862       { "viewport.playfield",   &viewport.playfield[i]  },
13863       { "viewport.door_1",      &viewport.door_1[i]     },
13864       { "viewport.door_2",      &viewport.door_2[i]     }
13865     };
13866
13867     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13868     {
13869       struct TokenIntPtrInfo vp_config[] =
13870       {
13871         { ".x",                 &vp_struct[j].struct_ptr->x             },
13872         { ".y",                 &vp_struct[j].struct_ptr->y             },
13873         { ".width",             &vp_struct[j].struct_ptr->width         },
13874         { ".height",            &vp_struct[j].struct_ptr->height        },
13875         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13876         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13877         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13878         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13879         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13880         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13881         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13882         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13883         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13884         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13885         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13886         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13887         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13888         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13889         { ".align",             &vp_struct[j].struct_ptr->align         },
13890         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13891       };
13892
13893       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13894       {
13895         char *token = getStringCat2(vp_struct[j].token_prefix,
13896                                     vp_config[k].token);
13897         char *value = getHashEntry(setup_file_hash, token);
13898
13899         if (value != NULL)
13900           *vp_config[k].value = get_token_parameter_value(token, value);
13901
13902         free(token);
13903       }
13904     }
13905   }
13906
13907   // special case: initialize with default values that may be overwritten
13908   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13909   for (i = 0; title_info[i].info != NULL; i++)
13910   {
13911     struct TitleFadingInfo *info = title_info[i].info;
13912     char *base_token = title_info[i].text;
13913
13914     for (j = 0; title_tokens[j].type != -1; j++)
13915     {
13916       char *token = getStringCat2(base_token, title_tokens[j].text);
13917       char *value = getHashEntry(setup_file_hash, token);
13918
13919       if (value != NULL)
13920       {
13921         int parameter_value = get_token_parameter_value(token, value);
13922
13923         tfi = *info;
13924
13925         *(int *)title_tokens[j].value = (int)parameter_value;
13926
13927         *info = tfi;
13928       }
13929
13930       free(token);
13931     }
13932   }
13933
13934   // special case: initialize with default values that may be overwritten
13935   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13936   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13937   {
13938     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13939     char *base_token = titlemessage_arrays[i].text;
13940
13941     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13942     {
13943       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13944       char *value = getHashEntry(setup_file_hash, token);
13945
13946       if (value != NULL)
13947       {
13948         int parameter_value = get_token_parameter_value(token, value);
13949
13950         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13951         {
13952           tmi = array[k];
13953
13954           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13955             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13956           else
13957             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13958
13959           array[k] = tmi;
13960         }
13961       }
13962
13963       free(token);
13964     }
13965   }
13966
13967   // read (and overwrite with) values that may be specified in config file
13968   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13969
13970   // special case: check if network and preview player positions are redefined
13971   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13972
13973   freeSetupFileHash(setup_file_hash);
13974 }
13975
13976 void LoadMenuDesignSettings(void)
13977 {
13978   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13979
13980   InitMenuDesignSettings_Static();
13981   InitMenuDesignSettings_SpecialPreProcessing();
13982   InitMenuDesignSettings_PreviewPlayers();
13983
13984   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13985   {
13986     // first look for special settings configured in level series config
13987     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13988
13989     if (fileExists(filename_base))
13990       LoadMenuDesignSettingsFromFilename(filename_base);
13991   }
13992
13993   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13994
13995   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13996     LoadMenuDesignSettingsFromFilename(filename_local);
13997
13998   InitMenuDesignSettings_SpecialPostProcessing();
13999 }
14000
14001 void LoadMenuDesignSettings_AfterGraphics(void)
14002 {
14003   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
14004 }
14005
14006 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
14007                                 boolean ignore_defaults)
14008 {
14009   int i;
14010
14011   for (i = 0; sound_config_vars[i].token != NULL; i++)
14012   {
14013     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
14014
14015     // (ignore definitions set to "[DEFAULT]" which are already initialized)
14016     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
14017       continue;
14018
14019     if (value != NULL)
14020       *sound_config_vars[i].value =
14021         get_token_parameter_value(sound_config_vars[i].token, value);
14022   }
14023 }
14024
14025 void InitSoundSettings_Static(void)
14026 {
14027   // always start with reliable default values from static default config
14028   InitSoundSettings_FromHash(sound_config_hash, FALSE);
14029 }
14030
14031 static void LoadSoundSettingsFromFilename(char *filename)
14032 {
14033   SetupFileHash *setup_file_hash;
14034
14035   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
14036     return;
14037
14038   // read (and overwrite with) values that may be specified in config file
14039   InitSoundSettings_FromHash(setup_file_hash, TRUE);
14040
14041   freeSetupFileHash(setup_file_hash);
14042 }
14043
14044 void LoadSoundSettings(void)
14045 {
14046   char *filename_base = UNDEFINED_FILENAME, *filename_local;
14047
14048   InitSoundSettings_Static();
14049
14050   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
14051   {
14052     // first look for special settings configured in level series config
14053     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
14054
14055     if (fileExists(filename_base))
14056       LoadSoundSettingsFromFilename(filename_base);
14057   }
14058
14059   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
14060
14061   if (filename_local != NULL && !strEqual(filename_base, filename_local))
14062     LoadSoundSettingsFromFilename(filename_local);
14063 }
14064
14065 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
14066 {
14067   char *filename = getEditorSetupFilename();
14068   SetupFileList *setup_file_list, *list;
14069   SetupFileHash *element_hash;
14070   int num_unknown_tokens = 0;
14071   int i;
14072
14073   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
14074     return;
14075
14076   element_hash = newSetupFileHash();
14077
14078   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14079     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14080
14081   // determined size may be larger than needed (due to unknown elements)
14082   *num_elements = 0;
14083   for (list = setup_file_list; list != NULL; list = list->next)
14084     (*num_elements)++;
14085
14086   // add space for up to 3 more elements for padding that may be needed
14087   *num_elements += 3;
14088
14089   // free memory for old list of elements, if needed
14090   checked_free(*elements);
14091
14092   // allocate memory for new list of elements
14093   *elements = checked_malloc(*num_elements * sizeof(int));
14094
14095   *num_elements = 0;
14096   for (list = setup_file_list; list != NULL; list = list->next)
14097   {
14098     char *value = getHashEntry(element_hash, list->token);
14099
14100     if (value == NULL)          // try to find obsolete token mapping
14101     {
14102       char *mapped_token = get_mapped_token(list->token);
14103
14104       if (mapped_token != NULL)
14105       {
14106         value = getHashEntry(element_hash, mapped_token);
14107
14108         free(mapped_token);
14109       }
14110     }
14111
14112     if (value != NULL)
14113     {
14114       (*elements)[(*num_elements)++] = atoi(value);
14115     }
14116     else
14117     {
14118       if (num_unknown_tokens == 0)
14119       {
14120         Warn("---");
14121         Warn("unknown token(s) found in config file:");
14122         Warn("- config file: '%s'", filename);
14123
14124         num_unknown_tokens++;
14125       }
14126
14127       Warn("- token: '%s'", list->token);
14128     }
14129   }
14130
14131   if (num_unknown_tokens > 0)
14132     Warn("---");
14133
14134   while (*num_elements % 4)     // pad with empty elements, if needed
14135     (*elements)[(*num_elements)++] = EL_EMPTY;
14136
14137   freeSetupFileList(setup_file_list);
14138   freeSetupFileHash(element_hash);
14139
14140 #if 0
14141   for (i = 0; i < *num_elements; i++)
14142     Debug("editor", "element '%s' [%d]\n",
14143           element_info[(*elements)[i]].token_name, (*elements)[i]);
14144 #endif
14145 }
14146
14147 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14148                                                      boolean is_sound)
14149 {
14150   SetupFileHash *setup_file_hash = NULL;
14151   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14152   char *filename_music, *filename_prefix, *filename_info;
14153   struct
14154   {
14155     char *token;
14156     char **value_ptr;
14157   }
14158   token_to_value_ptr[] =
14159   {
14160     { "title_header",   &tmp_music_file_info.title_header       },
14161     { "artist_header",  &tmp_music_file_info.artist_header      },
14162     { "album_header",   &tmp_music_file_info.album_header       },
14163     { "year_header",    &tmp_music_file_info.year_header        },
14164     { "played_header",  &tmp_music_file_info.played_header      },
14165
14166     { "title",          &tmp_music_file_info.title              },
14167     { "artist",         &tmp_music_file_info.artist             },
14168     { "album",          &tmp_music_file_info.album              },
14169     { "year",           &tmp_music_file_info.year               },
14170     { "played",         &tmp_music_file_info.played             },
14171
14172     { NULL,             NULL                                    },
14173   };
14174   int i;
14175
14176   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14177                     getCustomMusicFilename(basename));
14178
14179   if (filename_music == NULL)
14180     return NULL;
14181
14182   // ---------- try to replace file extension ----------
14183
14184   filename_prefix = getStringCopy(filename_music);
14185   if (strrchr(filename_prefix, '.') != NULL)
14186     *strrchr(filename_prefix, '.') = '\0';
14187   filename_info = getStringCat2(filename_prefix, ".txt");
14188
14189   if (fileExists(filename_info))
14190     setup_file_hash = loadSetupFileHash(filename_info);
14191
14192   free(filename_prefix);
14193   free(filename_info);
14194
14195   if (setup_file_hash == NULL)
14196   {
14197     // ---------- try to add file extension ----------
14198
14199     filename_prefix = getStringCopy(filename_music);
14200     filename_info = getStringCat2(filename_prefix, ".txt");
14201
14202     if (fileExists(filename_info))
14203       setup_file_hash = loadSetupFileHash(filename_info);
14204
14205     free(filename_prefix);
14206     free(filename_info);
14207   }
14208
14209   if (setup_file_hash == NULL)
14210     return NULL;
14211
14212   // ---------- music file info found ----------
14213
14214   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14215
14216   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14217   {
14218     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14219
14220     *token_to_value_ptr[i].value_ptr =
14221       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14222   }
14223
14224   tmp_music_file_info.basename = getStringCopy(basename);
14225   tmp_music_file_info.music = music;
14226   tmp_music_file_info.is_sound = is_sound;
14227
14228   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14229   *new_music_file_info = tmp_music_file_info;
14230
14231   return new_music_file_info;
14232 }
14233
14234 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14235 {
14236   return get_music_file_info_ext(basename, music, FALSE);
14237 }
14238
14239 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14240 {
14241   return get_music_file_info_ext(basename, sound, TRUE);
14242 }
14243
14244 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14245                                      char *basename, boolean is_sound)
14246 {
14247   for (; list != NULL; list = list->next)
14248     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14249       return TRUE;
14250
14251   return FALSE;
14252 }
14253
14254 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14255 {
14256   return music_info_listed_ext(list, basename, FALSE);
14257 }
14258
14259 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14260 {
14261   return music_info_listed_ext(list, basename, TRUE);
14262 }
14263
14264 void LoadMusicInfo(void)
14265 {
14266   int num_music_noconf = getMusicListSize_NoConf();
14267   int num_music = getMusicListSize();
14268   int num_sounds = getSoundListSize();
14269   struct FileInfo *music, *sound;
14270   struct MusicFileInfo *next, **new;
14271
14272   int i;
14273
14274   while (music_file_info != NULL)
14275   {
14276     next = music_file_info->next;
14277
14278     checked_free(music_file_info->basename);
14279
14280     checked_free(music_file_info->title_header);
14281     checked_free(music_file_info->artist_header);
14282     checked_free(music_file_info->album_header);
14283     checked_free(music_file_info->year_header);
14284     checked_free(music_file_info->played_header);
14285
14286     checked_free(music_file_info->title);
14287     checked_free(music_file_info->artist);
14288     checked_free(music_file_info->album);
14289     checked_free(music_file_info->year);
14290     checked_free(music_file_info->played);
14291
14292     free(music_file_info);
14293
14294     music_file_info = next;
14295   }
14296
14297   new = &music_file_info;
14298
14299   // get (configured or unconfigured) music file info for all levels
14300   for (i = leveldir_current->first_level;
14301        i <= leveldir_current->last_level; i++)
14302   {
14303     int music_nr;
14304
14305     if (levelset.music[i] != MUS_UNDEFINED)
14306     {
14307       // get music file info for configured level music
14308       music_nr = levelset.music[i];
14309     }
14310     else if (num_music_noconf > 0)
14311     {
14312       // get music file info for unconfigured level music
14313       int level_pos = i - leveldir_current->first_level;
14314
14315       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14316     }
14317     else
14318     {
14319       continue;
14320     }
14321
14322     char *basename = getMusicInfoEntryFilename(music_nr);
14323
14324     if (basename == NULL)
14325       continue;
14326
14327     if (!music_info_listed(music_file_info, basename))
14328     {
14329       *new = get_music_file_info(basename, music_nr);
14330
14331       if (*new != NULL)
14332         new = &(*new)->next;
14333     }
14334   }
14335
14336   // get music file info for all remaining configured music files
14337   for (i = 0; i < num_music; i++)
14338   {
14339     music = getMusicListEntry(i);
14340
14341     if (music->filename == NULL)
14342       continue;
14343
14344     if (strEqual(music->filename, UNDEFINED_FILENAME))
14345       continue;
14346
14347     // a configured file may be not recognized as music
14348     if (!FileIsMusic(music->filename))
14349       continue;
14350
14351     if (!music_info_listed(music_file_info, music->filename))
14352     {
14353       *new = get_music_file_info(music->filename, i);
14354
14355       if (*new != NULL)
14356         new = &(*new)->next;
14357     }
14358   }
14359
14360   // get sound file info for all configured sound files
14361   for (i = 0; i < num_sounds; i++)
14362   {
14363     sound = getSoundListEntry(i);
14364
14365     if (sound->filename == NULL)
14366       continue;
14367
14368     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14369       continue;
14370
14371     // a configured file may be not recognized as sound
14372     if (!FileIsSound(sound->filename))
14373       continue;
14374
14375     if (!sound_info_listed(music_file_info, sound->filename))
14376     {
14377       *new = get_sound_file_info(sound->filename, i);
14378       if (*new != NULL)
14379         new = &(*new)->next;
14380     }
14381   }
14382
14383   // add pointers to previous list nodes
14384
14385   struct MusicFileInfo *node = music_file_info;
14386
14387   while (node != NULL)
14388   {
14389     if (node->next)
14390       node->next->prev = node;
14391
14392     node = node->next;
14393   }
14394 }
14395
14396 static void add_helpanim_entry(int element, int action, int direction,
14397                                int delay, int *num_list_entries)
14398 {
14399   struct HelpAnimInfo *new_list_entry;
14400   (*num_list_entries)++;
14401
14402   helpanim_info =
14403     checked_realloc(helpanim_info,
14404                     *num_list_entries * sizeof(struct HelpAnimInfo));
14405   new_list_entry = &helpanim_info[*num_list_entries - 1];
14406
14407   new_list_entry->element = element;
14408   new_list_entry->action = action;
14409   new_list_entry->direction = direction;
14410   new_list_entry->delay = delay;
14411 }
14412
14413 static void print_unknown_token(char *filename, char *token, int token_nr)
14414 {
14415   if (token_nr == 0)
14416   {
14417     Warn("---");
14418     Warn("unknown token(s) found in config file:");
14419     Warn("- config file: '%s'", filename);
14420   }
14421
14422   Warn("- token: '%s'", token);
14423 }
14424
14425 static void print_unknown_token_end(int token_nr)
14426 {
14427   if (token_nr > 0)
14428     Warn("---");
14429 }
14430
14431 void LoadHelpAnimInfo(void)
14432 {
14433   char *filename = getHelpAnimFilename();
14434   SetupFileList *setup_file_list = NULL, *list;
14435   SetupFileHash *element_hash, *action_hash, *direction_hash;
14436   int num_list_entries = 0;
14437   int num_unknown_tokens = 0;
14438   int i;
14439
14440   if (fileExists(filename))
14441     setup_file_list = loadSetupFileList(filename);
14442
14443   if (setup_file_list == NULL)
14444   {
14445     // use reliable default values from static configuration
14446     SetupFileList *insert_ptr;
14447
14448     insert_ptr = setup_file_list =
14449       newSetupFileList(helpanim_config[0].token,
14450                        helpanim_config[0].value);
14451
14452     for (i = 1; helpanim_config[i].token; i++)
14453       insert_ptr = addListEntry(insert_ptr,
14454                                 helpanim_config[i].token,
14455                                 helpanim_config[i].value);
14456   }
14457
14458   element_hash   = newSetupFileHash();
14459   action_hash    = newSetupFileHash();
14460   direction_hash = newSetupFileHash();
14461
14462   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14463     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14464
14465   for (i = 0; i < NUM_ACTIONS; i++)
14466     setHashEntry(action_hash, element_action_info[i].suffix,
14467                  i_to_a(element_action_info[i].value));
14468
14469   // do not store direction index (bit) here, but direction value!
14470   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14471     setHashEntry(direction_hash, element_direction_info[i].suffix,
14472                  i_to_a(1 << element_direction_info[i].value));
14473
14474   for (list = setup_file_list; list != NULL; list = list->next)
14475   {
14476     char *element_token, *action_token, *direction_token;
14477     char *element_value, *action_value, *direction_value;
14478     int delay = atoi(list->value);
14479
14480     if (strEqual(list->token, "end"))
14481     {
14482       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14483
14484       continue;
14485     }
14486
14487     /* first try to break element into element/action/direction parts;
14488        if this does not work, also accept combined "element[.act][.dir]"
14489        elements (like "dynamite.active"), which are unique elements */
14490
14491     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14492     {
14493       element_value = getHashEntry(element_hash, list->token);
14494       if (element_value != NULL)        // element found
14495         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14496                            &num_list_entries);
14497       else
14498       {
14499         // no further suffixes found -- this is not an element
14500         print_unknown_token(filename, list->token, num_unknown_tokens++);
14501       }
14502
14503       continue;
14504     }
14505
14506     // token has format "<prefix>.<something>"
14507
14508     action_token = strchr(list->token, '.');    // suffix may be action ...
14509     direction_token = action_token;             // ... or direction
14510
14511     element_token = getStringCopy(list->token);
14512     *strchr(element_token, '.') = '\0';
14513
14514     element_value = getHashEntry(element_hash, element_token);
14515
14516     if (element_value == NULL)          // this is no element
14517     {
14518       element_value = getHashEntry(element_hash, list->token);
14519       if (element_value != NULL)        // combined element found
14520         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14521                            &num_list_entries);
14522       else
14523         print_unknown_token(filename, list->token, num_unknown_tokens++);
14524
14525       free(element_token);
14526
14527       continue;
14528     }
14529
14530     action_value = getHashEntry(action_hash, action_token);
14531
14532     if (action_value != NULL)           // action found
14533     {
14534       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14535                     &num_list_entries);
14536
14537       free(element_token);
14538
14539       continue;
14540     }
14541
14542     direction_value = getHashEntry(direction_hash, direction_token);
14543
14544     if (direction_value != NULL)        // direction found
14545     {
14546       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14547                          &num_list_entries);
14548
14549       free(element_token);
14550
14551       continue;
14552     }
14553
14554     if (strchr(action_token + 1, '.') == NULL)
14555     {
14556       // no further suffixes found -- this is not an action nor direction
14557
14558       element_value = getHashEntry(element_hash, list->token);
14559       if (element_value != NULL)        // combined element found
14560         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14561                            &num_list_entries);
14562       else
14563         print_unknown_token(filename, list->token, num_unknown_tokens++);
14564
14565       free(element_token);
14566
14567       continue;
14568     }
14569
14570     // token has format "<prefix>.<suffix>.<something>"
14571
14572     direction_token = strchr(action_token + 1, '.');
14573
14574     action_token = getStringCopy(action_token);
14575     *strchr(action_token + 1, '.') = '\0';
14576
14577     action_value = getHashEntry(action_hash, action_token);
14578
14579     if (action_value == NULL)           // this is no action
14580     {
14581       element_value = getHashEntry(element_hash, list->token);
14582       if (element_value != NULL)        // combined element found
14583         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14584                            &num_list_entries);
14585       else
14586         print_unknown_token(filename, list->token, num_unknown_tokens++);
14587
14588       free(element_token);
14589       free(action_token);
14590
14591       continue;
14592     }
14593
14594     direction_value = getHashEntry(direction_hash, direction_token);
14595
14596     if (direction_value != NULL)        // direction found
14597     {
14598       add_helpanim_entry(atoi(element_value), atoi(action_value),
14599                          atoi(direction_value), delay, &num_list_entries);
14600
14601       free(element_token);
14602       free(action_token);
14603
14604       continue;
14605     }
14606
14607     // this is no direction
14608
14609     element_value = getHashEntry(element_hash, list->token);
14610     if (element_value != NULL)          // combined element found
14611       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14612                          &num_list_entries);
14613     else
14614       print_unknown_token(filename, list->token, num_unknown_tokens++);
14615
14616     free(element_token);
14617     free(action_token);
14618   }
14619
14620   print_unknown_token_end(num_unknown_tokens);
14621
14622   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14623   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14624
14625   freeSetupFileList(setup_file_list);
14626   freeSetupFileHash(element_hash);
14627   freeSetupFileHash(action_hash);
14628   freeSetupFileHash(direction_hash);
14629
14630 #if 0
14631   for (i = 0; i < num_list_entries; i++)
14632     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14633           EL_NAME(helpanim_info[i].element),
14634           helpanim_info[i].element,
14635           helpanim_info[i].action,
14636           helpanim_info[i].direction,
14637           helpanim_info[i].delay);
14638 #endif
14639 }
14640
14641 void LoadHelpTextInfo(void)
14642 {
14643   char *filename = getHelpTextFilename();
14644   int i;
14645
14646   if (helptext_info != NULL)
14647   {
14648     freeSetupFileHash(helptext_info);
14649     helptext_info = NULL;
14650   }
14651
14652   if (fileExists(filename))
14653     helptext_info = loadSetupFileHash(filename);
14654
14655   if (helptext_info == NULL)
14656   {
14657     // use reliable default values from static configuration
14658     helptext_info = newSetupFileHash();
14659
14660     for (i = 0; helptext_config[i].token; i++)
14661       setHashEntry(helptext_info,
14662                    helptext_config[i].token,
14663                    helptext_config[i].value);
14664   }
14665
14666 #if 0
14667   BEGIN_HASH_ITERATION(helptext_info, itr)
14668   {
14669     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14670           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14671   }
14672   END_HASH_ITERATION(hash, itr)
14673 #endif
14674 }
14675
14676
14677 // ----------------------------------------------------------------------------
14678 // convert levels
14679 // ----------------------------------------------------------------------------
14680
14681 #define MAX_NUM_CONVERT_LEVELS          1000
14682
14683 void ConvertLevels(void)
14684 {
14685   static LevelDirTree *convert_leveldir = NULL;
14686   static int convert_level_nr = -1;
14687   static int num_levels_handled = 0;
14688   static int num_levels_converted = 0;
14689   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14690   int i;
14691
14692   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14693                                                global.convert_leveldir);
14694
14695   if (convert_leveldir == NULL)
14696     Fail("no such level identifier: '%s'", global.convert_leveldir);
14697
14698   leveldir_current = convert_leveldir;
14699
14700   if (global.convert_level_nr != -1)
14701   {
14702     convert_leveldir->first_level = global.convert_level_nr;
14703     convert_leveldir->last_level  = global.convert_level_nr;
14704   }
14705
14706   convert_level_nr = convert_leveldir->first_level;
14707
14708   PrintLine("=", 79);
14709   Print("Converting levels\n");
14710   PrintLine("-", 79);
14711   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14712   Print("Level series name:       '%s'\n", convert_leveldir->name);
14713   Print("Level series author:     '%s'\n", convert_leveldir->author);
14714   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14715   PrintLine("=", 79);
14716   Print("\n");
14717
14718   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14719     levels_failed[i] = FALSE;
14720
14721   while (convert_level_nr <= convert_leveldir->last_level)
14722   {
14723     char *level_filename;
14724     boolean new_level;
14725
14726     level_nr = convert_level_nr++;
14727
14728     Print("Level %03d: ", level_nr);
14729
14730     LoadLevel(level_nr);
14731     if (level.no_level_file || level.no_valid_file)
14732     {
14733       Print("(no level)\n");
14734       continue;
14735     }
14736
14737     Print("converting level ... ");
14738
14739 #if 0
14740     // special case: conversion of some EMC levels as requested by ACME
14741     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14742 #endif
14743
14744     level_filename = getDefaultLevelFilename(level_nr);
14745     new_level = !fileExists(level_filename);
14746
14747     if (new_level)
14748     {
14749       SaveLevel(level_nr);
14750
14751       num_levels_converted++;
14752
14753       Print("converted.\n");
14754     }
14755     else
14756     {
14757       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14758         levels_failed[level_nr] = TRUE;
14759
14760       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14761     }
14762
14763     num_levels_handled++;
14764   }
14765
14766   Print("\n");
14767   PrintLine("=", 79);
14768   Print("Number of levels handled: %d\n", num_levels_handled);
14769   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14770          (num_levels_handled ?
14771           num_levels_converted * 100 / num_levels_handled : 0));
14772   PrintLine("-", 79);
14773   Print("Summary (for automatic parsing by scripts):\n");
14774   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14775          convert_leveldir->identifier, num_levels_converted,
14776          num_levels_handled,
14777          (num_levels_handled ?
14778           num_levels_converted * 100 / num_levels_handled : 0));
14779
14780   if (num_levels_handled != num_levels_converted)
14781   {
14782     Print(", FAILED:");
14783     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14784       if (levels_failed[i])
14785         Print(" %03d", i);
14786   }
14787
14788   Print("\n");
14789   PrintLine("=", 79);
14790
14791   CloseAllAndExit(0);
14792 }
14793
14794
14795 // ----------------------------------------------------------------------------
14796 // create and save images for use in level sketches (raw BMP format)
14797 // ----------------------------------------------------------------------------
14798
14799 void CreateLevelSketchImages(void)
14800 {
14801   Bitmap *bitmap1;
14802   Bitmap *bitmap2;
14803   int i;
14804
14805   InitElementPropertiesGfxElement();
14806
14807   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14808   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14809
14810   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14811   {
14812     int element = getMappedElement(i);
14813     char basename1[16];
14814     char basename2[16];
14815     char *filename1;
14816     char *filename2;
14817
14818     sprintf(basename1, "%04d.bmp", i);
14819     sprintf(basename2, "%04ds.bmp", i);
14820
14821     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14822     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14823
14824     DrawSizedElement(0, 0, element, TILESIZE);
14825     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14826
14827     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14828       Fail("cannot save level sketch image file '%s'", filename1);
14829
14830     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14831     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14832
14833     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14834       Fail("cannot save level sketch image file '%s'", filename2);
14835
14836     free(filename1);
14837     free(filename2);
14838
14839     // create corresponding SQL statements (for normal and small images)
14840     if (i < 1000)
14841     {
14842       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14843       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14844     }
14845
14846     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14847     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14848
14849     // optional: create content for forum level sketch demonstration post
14850     if (options.debug)
14851       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14852   }
14853
14854   FreeBitmap(bitmap1);
14855   FreeBitmap(bitmap2);
14856
14857   if (options.debug)
14858     fprintf(stderr, "\n");
14859
14860   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14861
14862   CloseAllAndExit(0);
14863 }
14864
14865
14866 // ----------------------------------------------------------------------------
14867 // create and save images for element collecting animations (raw BMP format)
14868 // ----------------------------------------------------------------------------
14869
14870 static boolean createCollectImage(int element)
14871 {
14872   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14873 }
14874
14875 void CreateCollectElementImages(void)
14876 {
14877   int i, j;
14878   int num_steps = 8;
14879   int anim_frames = num_steps - 1;
14880   int tile_size = TILESIZE;
14881   int anim_width  = tile_size * anim_frames;
14882   int anim_height = tile_size;
14883   int num_collect_images = 0;
14884   int pos_collect_images = 0;
14885
14886   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14887     if (createCollectImage(i))
14888       num_collect_images++;
14889
14890   Info("Creating %d element collecting animation images ...",
14891        num_collect_images);
14892
14893   int dst_width  = anim_width * 2;
14894   int dst_height = anim_height * num_collect_images / 2;
14895   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14896   char *basename_bmp = "RocksCollect.bmp";
14897   char *basename_png = "RocksCollect.png";
14898   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14899   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14900   int len_filename_bmp = strlen(filename_bmp);
14901   int len_filename_png = strlen(filename_png);
14902   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14903   char cmd_convert[max_command_len];
14904
14905   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14906            filename_bmp,
14907            filename_png);
14908
14909   // force using RGBA surface for destination bitmap
14910   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14911                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14912
14913   dst_bitmap->surface =
14914     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14915
14916   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14917   {
14918     if (!createCollectImage(i))
14919       continue;
14920
14921     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14922     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14923     int graphic = el2img(i);
14924     char *token_name = element_info[i].token_name;
14925     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14926     Bitmap *src_bitmap;
14927     int src_x, src_y;
14928
14929     Info("- creating collecting image for '%s' ...", token_name);
14930
14931     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14932
14933     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14934                tile_size, tile_size, 0, 0);
14935
14936     // force using RGBA surface for temporary bitmap (using transparent black)
14937     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14938                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14939
14940     tmp_bitmap->surface =
14941       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14942
14943     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14944
14945     for (j = 0; j < anim_frames; j++)
14946     {
14947       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14948       int frame_size = frame_size_final * num_steps;
14949       int offset = (tile_size - frame_size_final) / 2;
14950       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14951
14952       while (frame_size > frame_size_final)
14953       {
14954         frame_size /= 2;
14955
14956         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14957
14958         FreeBitmap(frame_bitmap);
14959
14960         frame_bitmap = half_bitmap;
14961       }
14962
14963       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14964                        frame_size_final, frame_size_final,
14965                        dst_x + j * tile_size + offset, dst_y + offset);
14966
14967       FreeBitmap(frame_bitmap);
14968     }
14969
14970     tmp_bitmap->surface_masked = NULL;
14971
14972     FreeBitmap(tmp_bitmap);
14973
14974     pos_collect_images++;
14975   }
14976
14977   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14978     Fail("cannot save element collecting image file '%s'", filename_bmp);
14979
14980   FreeBitmap(dst_bitmap);
14981
14982   Info("Converting image file from BMP to PNG ...");
14983
14984   if (system(cmd_convert) != 0)
14985     Fail("converting image file failed");
14986
14987   unlink(filename_bmp);
14988
14989   Info("Done.");
14990
14991   CloseAllAndExit(0);
14992 }
14993
14994
14995 // ----------------------------------------------------------------------------
14996 // create and save images for custom and group elements (raw BMP format)
14997 // ----------------------------------------------------------------------------
14998
14999 void CreateCustomElementImages(char *directory)
15000 {
15001   char *src_basename = "RocksCE-template.ilbm";
15002   char *dst_basename = "RocksCE.bmp";
15003   char *src_filename = getPath2(directory, src_basename);
15004   char *dst_filename = getPath2(directory, dst_basename);
15005   Bitmap *src_bitmap;
15006   Bitmap *bitmap;
15007   int yoffset_ce = 0;
15008   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
15009   int i;
15010
15011   InitVideoDefaults();
15012
15013   ReCreateBitmap(&backbuffer, video.width, video.height);
15014
15015   src_bitmap = LoadImage(src_filename);
15016
15017   bitmap = CreateBitmap(TILEX * 16 * 2,
15018                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
15019                         DEFAULT_DEPTH);
15020
15021   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15022   {
15023     int x = i % 16;
15024     int y = i / 16;
15025     int ii = i + 1;
15026     int j;
15027
15028     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15029                TILEX * x, TILEY * y + yoffset_ce);
15030
15031     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15032                TILEX, TILEY,
15033                TILEX * x + TILEX * 16,
15034                TILEY * y + yoffset_ce);
15035
15036     for (j = 2; j >= 0; j--)
15037     {
15038       int c = ii % 10;
15039
15040       BlitBitmap(src_bitmap, bitmap,
15041                  TILEX + c * 7, 0, 6, 10,
15042                  TILEX * x + 6 + j * 7,
15043                  TILEY * y + 11 + yoffset_ce);
15044
15045       BlitBitmap(src_bitmap, bitmap,
15046                  TILEX + c * 8, TILEY, 6, 10,
15047                  TILEX * 16 + TILEX * x + 6 + j * 8,
15048                  TILEY * y + 10 + yoffset_ce);
15049
15050       ii /= 10;
15051     }
15052   }
15053
15054   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15055   {
15056     int x = i % 16;
15057     int y = i / 16;
15058     int ii = i + 1;
15059     int j;
15060
15061     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
15062                TILEX * x, TILEY * y + yoffset_ge);
15063
15064     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
15065                TILEX, TILEY,
15066                TILEX * x + TILEX * 16,
15067                TILEY * y + yoffset_ge);
15068
15069     for (j = 1; j >= 0; j--)
15070     {
15071       int c = ii % 10;
15072
15073       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
15074                  TILEX * x + 6 + j * 10,
15075                  TILEY * y + 11 + yoffset_ge);
15076
15077       BlitBitmap(src_bitmap, bitmap,
15078                  TILEX + c * 8, TILEY + 12, 6, 10,
15079                  TILEX * 16 + TILEX * x + 10 + j * 8,
15080                  TILEY * y + 10 + yoffset_ge);
15081
15082       ii /= 10;
15083     }
15084   }
15085
15086   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
15087     Fail("cannot save CE graphics file '%s'", dst_filename);
15088
15089   FreeBitmap(bitmap);
15090
15091   CloseAllAndExit(0);
15092 }