added also loading non-successful replay when loading native BD level
[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 "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_INTEGER,                       CONF_VALUE_8_BIT(23),
311     &li.bd_cave_random_seed_c64,        0
312   },
313
314   {
315     -1,                                 -1,
316     -1,                                 -1,
317     NULL,                               -1
318   }
319 };
320
321 static struct LevelFileConfigInfo chunk_config_ELEM[] =
322 {
323   // (these values are the same for each player)
324   {
325     EL_PLAYER_1,                        -1,
326     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
327     &li.block_last_field,               FALSE   // default case for EM levels
328   },
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
332     &li.sp_block_last_field,            TRUE    // default case for SP levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
337     &li.instant_relocation,             FALSE
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
342     &li.can_pass_to_walkable,           FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
347     &li.block_snap_field,               TRUE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
352     &li.continuous_snapping,            TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
357     &li.shifted_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
362     &li.lazy_relocation,                FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
367     &li.finish_dig_collect,             TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
372     &li.keep_walkable_ce,               FALSE
373   },
374
375   // (these values are different for each player)
376   {
377     EL_PLAYER_1,                        -1,
378     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
379     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
380   },
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
384     &li.initial_player_gravity[0],      FALSE
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
389     &li.use_start_element[0],           FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
394     &li.start_element[0],               EL_PLAYER_1
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
399     &li.use_artwork_element[0],         FALSE
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
404     &li.artwork_element[0],             EL_PLAYER_1
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
409     &li.use_explosion_element[0],       FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
414     &li.explosion_element[0],           EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
419     &li.use_initial_inventory[0],       FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
424     &li.initial_inventory_size[0],      1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
429     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
430     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
431   },
432
433   {
434     EL_PLAYER_2,                        -1,
435     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
436     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
437   },
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
441     &li.initial_player_gravity[1],      FALSE
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
446     &li.use_start_element[1],           FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
451     &li.start_element[1],               EL_PLAYER_2
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
456     &li.use_artwork_element[1],         FALSE
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
461     &li.artwork_element[1],             EL_PLAYER_2
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
466     &li.use_explosion_element[1],       FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
471     &li.explosion_element[1],           EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
476     &li.use_initial_inventory[1],       FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
481     &li.initial_inventory_size[1],      1
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
486     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
487     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
488   },
489
490   {
491     EL_PLAYER_3,                        -1,
492     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
493     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
494   },
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
498     &li.initial_player_gravity[2],      FALSE
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
503     &li.use_start_element[2],           FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
508     &li.start_element[2],               EL_PLAYER_3
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
513     &li.use_artwork_element[2],         FALSE
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
518     &li.artwork_element[2],             EL_PLAYER_3
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
523     &li.use_explosion_element[2],       FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
528     &li.explosion_element[2],           EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
533     &li.use_initial_inventory[2],       FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
538     &li.initial_inventory_size[2],      1
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
543     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
544     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
545   },
546
547   {
548     EL_PLAYER_4,                        -1,
549     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
550     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
551   },
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
555     &li.initial_player_gravity[3],      FALSE
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
560     &li.use_start_element[3],           FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
565     &li.start_element[3],               EL_PLAYER_4
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
570     &li.use_artwork_element[3],         FALSE
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
575     &li.artwork_element[3],             EL_PLAYER_4
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
580     &li.use_explosion_element[3],       FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
585     &li.explosion_element[3],           EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
590     &li.use_initial_inventory[3],       FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
595     &li.initial_inventory_size[3],      1
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
600     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
601     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
602   },
603
604   // (these values are only valid for BD style levels)
605   // (some values for BD style amoeba following below)
606   {
607     EL_BD_PLAYER,                       -1,
608     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
609     &li.bd_diagonal_movements,          FALSE
610   },
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
614     &li.bd_topmost_player_active,       TRUE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
619     &li.bd_pushing_prob,                25
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
624     &li.bd_pushing_prob_with_sweet,     100
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
629     &li.bd_push_mega_rock_with_sweet,   FALSE
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
634     &li.bd_snap_element,                EL_EMPTY
635   },
636
637   {
638     EL_BD_SAND,                         -1,
639     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
640     &li.bd_sand_looks_like,             EL_BD_SAND
641   },
642
643   {
644     EL_BD_ROCK,                         -1,
645     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
646     &li.bd_rock_turns_to_on_falling,    EL_BD_ROCK_FALLING
647   },
648   {
649     EL_BD_ROCK,                         -1,
650     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
651     &li.bd_rock_turns_to_on_impact,     EL_BD_ROCK
652   },
653
654   {
655     EL_BD_DIAMOND,                      -1,
656     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
657     &li.score[SC_DIAMOND_EXTRA],        20
658   },
659   {
660     EL_BD_DIAMOND,                      -1,
661     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
662     &li.bd_diamond_turns_to_on_falling, EL_BD_DIAMOND_FALLING
663   },
664   {
665     EL_BD_DIAMOND,                      -1,
666     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
667     &li.bd_diamond_turns_to_on_impact,  EL_BD_DIAMOND
668   },
669
670   {
671     EL_BD_FIREFLY,                      -1,
672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
673     &li.bd_firefly_explodes_to,         EL_BD_EXPLODING_1
674   },
675
676   {
677     EL_BD_FIREFLY_2,                    -1,
678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
679     &li.bd_firefly_2_explodes_to,       EL_BD_EXPLODING_1
680   },
681
682   {
683     EL_BD_BUTTERFLY,                    -1,
684     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
685     &li.bd_butterfly_explodes_to,       EL_BD_DIAMOND_GROWING_1
686   },
687
688   {
689     EL_BD_BUTTERFLY_2,                  -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
691     &li.bd_butterfly_2_explodes_to,     EL_BD_DIAMOND_GROWING_1
692   },
693
694   {
695     EL_BD_STONEFLY,                     -1,
696     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
697     &li.bd_stonefly_explodes_to,        EL_BD_ROCK_GROWING_1
698   },
699
700   {
701     EL_BD_DRAGONFLY,                    -1,
702     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
703     &li.bd_dragonfly_explodes_to,       EL_BD_EXPLODING_1
704   },
705
706   {
707     EL_BD_DIAMOND_GROWING_5,            -1,
708     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
709     &li.bd_diamond_birth_turns_to,      EL_BD_DIAMOND
710   },
711
712   {
713     EL_BD_BOMB_EXPLODING_4,             -1,
714     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
715     &li.bd_bomb_explosion_turns_to,     EL_BD_WALL
716   },
717
718   {
719     EL_BD_NITRO_PACK_EXPLODING_4,       -1,
720     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
721     &li.bd_nitro_explosion_turns_to,    EL_EMPTY
722   },
723
724   {
725     EL_BD_EXPLODING_5,                  -1,
726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
727     &li.bd_explosion_turns_to,          EL_EMPTY
728   },
729
730   {
731     EL_BD_MAGIC_WALL,                   -1,
732     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
733     &li.bd_magic_wall_wait_hatching,    FALSE
734   },
735   {
736     EL_BD_MAGIC_WALL,                   -1,
737     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
738     &li.bd_magic_wall_stops_amoeba,     TRUE
739   },
740   {
741     EL_BD_MAGIC_WALL,                   -1,
742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
743     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
744   },
745   {
746     EL_BD_MAGIC_WALL,                   -1,
747     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
748     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
749   },
750   {
751     EL_BD_MAGIC_WALL,                   -1,
752     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
753     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
754   },
755   {
756     EL_BD_MAGIC_WALL,                   -1,
757     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
758     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
759   },
760   {
761     EL_BD_MAGIC_WALL,                   -1,
762     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
763     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
764   },
765   {
766     EL_BD_MAGIC_WALL,                   -1,
767     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
768     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
769   },
770   {
771     EL_BD_MAGIC_WALL,                   -1,
772     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
773     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
774   },
775
776   {
777     EL_BD_CLOCK,                        -1,
778     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
779     &li.bd_clock_extra_time,            30
780   },
781
782   {
783     EL_BD_VOODOO_DOLL,                  -1,
784     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
785     &li.bd_voodoo_collects_diamonds,    FALSE
786   },
787   {
788     EL_BD_VOODOO_DOLL,                  -1,
789     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
790     &li.bd_voodoo_hurt_kills_player,    FALSE
791   },
792   {
793     EL_BD_VOODOO_DOLL,                  -1,
794     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
795     &li.bd_voodoo_dies_by_rock,         FALSE
796   },
797   {
798     EL_BD_VOODOO_DOLL,                  -1,
799     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
800     &li.bd_voodoo_vanish_by_explosion,  TRUE
801   },
802   {
803     EL_BD_VOODOO_DOLL,                  -1,
804     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
805     &li.bd_voodoo_penalty_time,         30
806   },
807
808   {
809     EL_BD_SLIME,                        -1,
810     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
811     &li.bd_slime_is_predictable,        TRUE
812   },
813   {
814     EL_BD_SLIME,                        -1,
815     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
816     &li.bd_slime_permeability_rate,     100
817   },
818   {
819     EL_BD_SLIME,                        -1,
820     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
821     &li.bd_slime_permeability_bits_c64, 0
822   },
823   {
824     EL_BD_SLIME,                        -1,
825     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
826     &li.bd_slime_random_seed_c64,       -1
827   },
828   {
829     EL_BD_SLIME,                        -1,
830     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
831     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
832   },
833   {
834     EL_BD_SLIME,                        -1,
835     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
836     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
837   },
838   {
839     EL_BD_SLIME,                        -1,
840     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
841     &li.bd_slime_eats_element_2,        EL_BD_ROCK
842   },
843   {
844     EL_BD_SLIME,                        -1,
845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
846     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
847   },
848   {
849     EL_BD_SLIME,                        -1,
850     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
851     &li.bd_slime_eats_element_3,        EL_BD_NUT
852   },
853   {
854     EL_BD_SLIME,                        -1,
855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
856     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
857   },
858
859   {
860     EL_BD_ACID,                         -1,
861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
862     &li.bd_acid_eats_element,           EL_BD_SAND
863   },
864   {
865     EL_BD_ACID,                         -1,
866     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
867     &li.bd_acid_spread_rate,            3
868   },
869   {
870     EL_BD_ACID,                         -1,
871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
872     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
873   },
874
875   {
876     EL_BD_BITER,                        -1,
877     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
878     &li.bd_biter_move_delay,            0
879   },
880   {
881     EL_BD_BITER,                        -1,
882     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
883     &li.bd_biter_eats_element,          EL_BD_DIAMOND
884   },
885
886   {
887     EL_BD_BLADDER,                      -1,
888     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
889     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
890   },
891
892   {
893     EL_BD_EXPANDABLE_WALL_ANY,          -1,
894     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
895     &li.bd_change_expanding_wall,       FALSE
896   },
897   {
898     EL_BD_EXPANDABLE_WALL_ANY,          -1,
899     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
900     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
901   },
902
903   {
904     EL_BD_REPLICATOR,                   -1,
905     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
906     &li.bd_replicators_active,          TRUE
907   },
908   {
909     EL_BD_REPLICATOR,                   -1,
910     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
911     &li.bd_replicator_create_delay,     4
912   },
913
914   {
915     EL_BD_CONVEYOR_LEFT,                -1,
916     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
917     &li.bd_conveyor_belts_active,       TRUE
918   },
919   {
920     EL_BD_CONVEYOR_LEFT,                -1,
921     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
922     &li.bd_conveyor_belts_changed,      FALSE
923   },
924
925   {
926     EL_BD_WATER,                        -1,
927     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
928     &li.bd_water_cannot_flow_down,      FALSE
929   },
930
931   {
932     EL_BD_NUT,                          -1,
933     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
934     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
935   },
936
937   {
938     EL_BD_PNEUMATIC_HAMMER,             -1,
939     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
940     &li.bd_hammer_walls_break_delay,    5
941   },
942   {
943     EL_BD_PNEUMATIC_HAMMER,             -1,
944     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
945     &li.bd_hammer_walls_reappear,       FALSE
946   },
947   {
948     EL_BD_PNEUMATIC_HAMMER,             -1,
949     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
950     &li.bd_hammer_walls_reappear_delay, 100
951   },
952
953   {
954     EL_BD_ROCKET_LAUNCHER,              -1,
955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
956     &li.bd_infinite_rockets,            FALSE
957   },
958
959   {
960     EL_BD_SKELETON,                     -1,
961     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
962     &li.bd_num_skeletons_needed_for_pot, 5
963   },
964   {
965     EL_BD_SKELETON,                     -1,
966     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
967     &li.bd_skeleton_worth_num_diamonds, 0
968   },
969
970   {
971     EL_BD_CREATURE_SWITCH,              -1,
972     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
973     &li.bd_creatures_start_backwards,   FALSE
974   },
975   {
976     EL_BD_CREATURE_SWITCH,              -1,
977     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
978     &li.bd_creatures_turn_on_hatching,  FALSE
979   },
980   {
981     EL_BD_CREATURE_SWITCH,              -1,
982     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
983     &li.bd_creatures_auto_turn_delay,   0
984   },
985
986   {
987     EL_BD_GRAVITY_SWITCH,               -1,
988     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
989     &li.bd_gravity_direction,           GD_MV_DOWN
990   },
991   {
992     EL_BD_GRAVITY_SWITCH,               -1,
993     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
994     &li.bd_gravity_switch_active,       FALSE
995   },
996   {
997     EL_BD_GRAVITY_SWITCH,               -1,
998     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
999     &li.bd_gravity_switch_delay,        10
1000   },
1001   {
1002     EL_BD_GRAVITY_SWITCH,               -1,
1003     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1004     &li.bd_gravity_affects_all,         TRUE
1005   },
1006
1007   // (the following values are related to various game elements)
1008
1009   {
1010     EL_EMERALD,                         -1,
1011     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1012     &li.score[SC_EMERALD],              10
1013   },
1014
1015   {
1016     EL_DIAMOND,                         -1,
1017     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1018     &li.score[SC_DIAMOND],              10
1019   },
1020
1021   {
1022     EL_BUG,                             -1,
1023     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1024     &li.score[SC_BUG],                  10
1025   },
1026
1027   {
1028     EL_SPACESHIP,                       -1,
1029     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1030     &li.score[SC_SPACESHIP],            10
1031   },
1032
1033   {
1034     EL_PACMAN,                          -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1036     &li.score[SC_PACMAN],               10
1037   },
1038
1039   {
1040     EL_NUT,                             -1,
1041     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1042     &li.score[SC_NUT],                  10
1043   },
1044
1045   {
1046     EL_DYNAMITE,                        -1,
1047     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1048     &li.score[SC_DYNAMITE],             10
1049   },
1050
1051   {
1052     EL_KEY_1,                           -1,
1053     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1054     &li.score[SC_KEY],                  10
1055   },
1056
1057   {
1058     EL_PEARL,                           -1,
1059     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1060     &li.score[SC_PEARL],                10
1061   },
1062
1063   {
1064     EL_CRYSTAL,                         -1,
1065     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1066     &li.score[SC_CRYSTAL],              10
1067   },
1068
1069   // (amoeba values used by R'n'D game engine only)
1070   {
1071     EL_BD_AMOEBA,                       -1,
1072     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1073     &li.amoeba_content,                 EL_DIAMOND
1074   },
1075   {
1076     EL_BD_AMOEBA,                       -1,
1077     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1078     &li.amoeba_speed,                   10
1079   },
1080   {
1081     EL_BD_AMOEBA,                       -1,
1082     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1083     &li.grow_into_diggable,             TRUE
1084   },
1085   // (amoeba values used by BD game engine only)
1086   {
1087     EL_BD_AMOEBA,                       -1,
1088     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1089     &li.bd_amoeba_wait_for_hatching,    FALSE
1090   },
1091   {
1092     EL_BD_AMOEBA,                       -1,
1093     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1094     &li.bd_amoeba_start_immediately,    TRUE
1095   },
1096   {
1097     EL_BD_AMOEBA,                       -1,
1098     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1099     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
1100   },
1101   {
1102     EL_BD_AMOEBA,                       -1,
1103     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1104     &li.bd_amoeba_threshold_too_big,    200
1105   },
1106   {
1107     EL_BD_AMOEBA,                       -1,
1108     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1109     &li.bd_amoeba_slow_growth_time,     200
1110   },
1111   {
1112     EL_BD_AMOEBA,                       -1,
1113     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1114     &li.bd_amoeba_slow_growth_rate,     3
1115   },
1116   {
1117     EL_BD_AMOEBA,                       -1,
1118     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1119     &li.bd_amoeba_fast_growth_rate,     25
1120   },
1121   {
1122     EL_BD_AMOEBA,                       -1,
1123     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1124     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1125   },
1126   {
1127     EL_BD_AMOEBA,                       -1,
1128     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1129     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1130   },
1131
1132   {
1133     EL_BD_AMOEBA_2,                     -1,
1134     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1135     &li.bd_amoeba_2_threshold_too_big,  200
1136   },
1137   {
1138     EL_BD_AMOEBA_2,                     -1,
1139     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1140     &li.bd_amoeba_2_slow_growth_time,   200
1141   },
1142   {
1143     EL_BD_AMOEBA_2,                     -1,
1144     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1145     &li.bd_amoeba_2_slow_growth_rate,   3
1146   },
1147   {
1148     EL_BD_AMOEBA_2,                     -1,
1149     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1150     &li.bd_amoeba_2_fast_growth_rate,   25
1151   },
1152   {
1153     EL_BD_AMOEBA_2,                     -1,
1154     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1155     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1156   },
1157   {
1158     EL_BD_AMOEBA_2,                     -1,
1159     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1160     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1161   },
1162   {
1163     EL_BD_AMOEBA_2,                     -1,
1164     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1165     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1166   },
1167   {
1168     EL_BD_AMOEBA_2,                     -1,
1169     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1170     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1171   },
1172
1173   {
1174     EL_YAMYAM,                          -1,
1175     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1176     &li.yamyam_content,                 EL_ROCK, NULL,
1177     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1178   },
1179   {
1180     EL_YAMYAM,                          -1,
1181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1182     &li.score[SC_YAMYAM],               10
1183   },
1184
1185   {
1186     EL_ROBOT,                           -1,
1187     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1188     &li.score[SC_ROBOT],                10
1189   },
1190   {
1191     EL_ROBOT,                           -1,
1192     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1193     &li.slurp_score,                    10
1194   },
1195
1196   {
1197     EL_ROBOT_WHEEL,                     -1,
1198     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1199     &li.time_wheel,                     10
1200   },
1201
1202   {
1203     EL_MAGIC_WALL,                      -1,
1204     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1205     &li.time_magic_wall,                10
1206   },
1207
1208   {
1209     EL_GAME_OF_LIFE,                    -1,
1210     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1211     &li.game_of_life[0],                2
1212   },
1213   {
1214     EL_GAME_OF_LIFE,                    -1,
1215     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1216     &li.game_of_life[1],                3
1217   },
1218   {
1219     EL_GAME_OF_LIFE,                    -1,
1220     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1221     &li.game_of_life[2],                3
1222   },
1223   {
1224     EL_GAME_OF_LIFE,                    -1,
1225     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1226     &li.game_of_life[3],                3
1227   },
1228   {
1229     EL_GAME_OF_LIFE,                    -1,
1230     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1231     &li.use_life_bugs,                  FALSE
1232   },
1233
1234   {
1235     EL_BIOMAZE,                         -1,
1236     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1237     &li.biomaze[0],                     2
1238   },
1239   {
1240     EL_BIOMAZE,                         -1,
1241     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1242     &li.biomaze[1],                     3
1243   },
1244   {
1245     EL_BIOMAZE,                         -1,
1246     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1247     &li.biomaze[2],                     3
1248   },
1249   {
1250     EL_BIOMAZE,                         -1,
1251     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1252     &li.biomaze[3],                     3
1253   },
1254
1255   {
1256     EL_TIMEGATE_SWITCH,                 -1,
1257     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1258     &li.time_timegate,                  10
1259   },
1260
1261   {
1262     EL_LIGHT_SWITCH_ACTIVE,             -1,
1263     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1264     &li.time_light,                     10
1265   },
1266
1267   {
1268     EL_SHIELD_NORMAL,                   -1,
1269     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1270     &li.shield_normal_time,             10
1271   },
1272   {
1273     EL_SHIELD_NORMAL,                   -1,
1274     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1275     &li.score[SC_SHIELD],               10
1276   },
1277
1278   {
1279     EL_SHIELD_DEADLY,                   -1,
1280     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1281     &li.shield_deadly_time,             10
1282   },
1283   {
1284     EL_SHIELD_DEADLY,                   -1,
1285     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1286     &li.score[SC_SHIELD],               10
1287   },
1288
1289   {
1290     EL_EXTRA_TIME,                      -1,
1291     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1292     &li.extra_time,                     10
1293   },
1294   {
1295     EL_EXTRA_TIME,                      -1,
1296     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1297     &li.extra_time_score,               10
1298   },
1299
1300   {
1301     EL_TIME_ORB_FULL,                   -1,
1302     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1303     &li.time_orb_time,                  10
1304   },
1305   {
1306     EL_TIME_ORB_FULL,                   -1,
1307     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1308     &li.use_time_orb_bug,               FALSE
1309   },
1310
1311   {
1312     EL_SPRING,                          -1,
1313     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1314     &li.use_spring_bug,                 FALSE
1315   },
1316
1317   {
1318     EL_EMC_ANDROID,                     -1,
1319     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1320     &li.android_move_time,              10
1321   },
1322   {
1323     EL_EMC_ANDROID,                     -1,
1324     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1325     &li.android_clone_time,             10
1326   },
1327   {
1328     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1329     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1330     &li.android_clone_element[0],       EL_EMPTY, NULL,
1331     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1332   },
1333   {
1334     EL_EMC_ANDROID,                     -1,
1335     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1336     &li.android_clone_element[0],       EL_EMPTY, NULL,
1337     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1338   },
1339
1340   {
1341     EL_EMC_LENSES,                      -1,
1342     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1343     &li.lenses_score,                   10
1344   },
1345   {
1346     EL_EMC_LENSES,                      -1,
1347     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1348     &li.lenses_time,                    10
1349   },
1350
1351   {
1352     EL_EMC_MAGNIFIER,                   -1,
1353     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1354     &li.magnify_score,                  10
1355   },
1356   {
1357     EL_EMC_MAGNIFIER,                   -1,
1358     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1359     &li.magnify_time,                   10
1360   },
1361
1362   {
1363     EL_EMC_MAGIC_BALL,                  -1,
1364     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1365     &li.ball_time,                      10
1366   },
1367   {
1368     EL_EMC_MAGIC_BALL,                  -1,
1369     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1370     &li.ball_random,                    FALSE
1371   },
1372   {
1373     EL_EMC_MAGIC_BALL,                  -1,
1374     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1375     &li.ball_active_initial,            FALSE
1376   },
1377   {
1378     EL_EMC_MAGIC_BALL,                  -1,
1379     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1380     &li.ball_content,                   EL_EMPTY, NULL,
1381     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1382   },
1383
1384   {
1385     EL_SOKOBAN_FIELD_EMPTY,             -1,
1386     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1387     &li.sb_fields_needed,               TRUE
1388   },
1389
1390   {
1391     EL_SOKOBAN_OBJECT,                  -1,
1392     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1393     &li.sb_objects_needed,              TRUE
1394   },
1395
1396   {
1397     EL_MM_MCDUFFIN,                     -1,
1398     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1399     &li.mm_laser_red,                   FALSE
1400   },
1401   {
1402     EL_MM_MCDUFFIN,                     -1,
1403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1404     &li.mm_laser_green,                 FALSE
1405   },
1406   {
1407     EL_MM_MCDUFFIN,                     -1,
1408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1409     &li.mm_laser_blue,                  TRUE
1410   },
1411
1412   {
1413     EL_DF_LASER,                        -1,
1414     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1415     &li.df_laser_red,                   TRUE
1416   },
1417   {
1418     EL_DF_LASER,                        -1,
1419     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1420     &li.df_laser_green,                 TRUE
1421   },
1422   {
1423     EL_DF_LASER,                        -1,
1424     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1425     &li.df_laser_blue,                  FALSE
1426   },
1427
1428   {
1429     EL_MM_FUSE_ACTIVE,                  -1,
1430     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1431     &li.mm_time_fuse,                   25
1432   },
1433   {
1434     EL_MM_BOMB,                         -1,
1435     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1436     &li.mm_time_bomb,                   75
1437   },
1438
1439   {
1440     EL_MM_GRAY_BALL,                    -1,
1441     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1442     &li.mm_time_ball,                   75
1443   },
1444   {
1445     EL_MM_GRAY_BALL,                    -1,
1446     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1447     &li.mm_ball_choice_mode,            ANIM_RANDOM
1448   },
1449   {
1450     EL_MM_GRAY_BALL,                    -1,
1451     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1452     &li.mm_ball_content,                EL_EMPTY, NULL,
1453     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1454   },
1455   {
1456     EL_MM_GRAY_BALL,                    -1,
1457     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1458     &li.rotate_mm_ball_content,         TRUE
1459   },
1460   {
1461     EL_MM_GRAY_BALL,                    -1,
1462     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1463     &li.explode_mm_ball,                FALSE
1464   },
1465
1466   {
1467     EL_MM_STEEL_BLOCK,                  -1,
1468     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1469     &li.mm_time_block,                  75
1470   },
1471   {
1472     EL_MM_LIGHTBALL,                    -1,
1473     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1474     &li.score[SC_ELEM_BONUS],           10
1475   },
1476
1477   {
1478     -1,                                 -1,
1479     -1,                                 -1,
1480     NULL,                               -1
1481   }
1482 };
1483
1484 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1485 {
1486   {
1487     -1,                                 -1,
1488     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1489     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1490   },
1491   {
1492     -1,                                 -1,
1493     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1494     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1495   },
1496
1497   {
1498     -1,                                 -1,
1499     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1500     &xx_envelope.autowrap,              FALSE
1501   },
1502   {
1503     -1,                                 -1,
1504     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1505     &xx_envelope.centered,              FALSE
1506   },
1507
1508   {
1509     -1,                                 -1,
1510     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1511     &xx_envelope.text,                  -1, NULL,
1512     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1513     &xx_default_string_empty[0]
1514   },
1515
1516   {
1517     -1,                                 -1,
1518     -1,                                 -1,
1519     NULL,                               -1
1520   }
1521 };
1522
1523 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1524 {
1525   {
1526     -1,                                 -1,
1527     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1528     &xx_ei.description[0],              -1,
1529     &yy_ei.description[0],
1530     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1531     &xx_default_description[0]
1532   },
1533
1534   {
1535     -1,                                 -1,
1536     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1537     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1538     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1539   },
1540 #if ENABLE_RESERVED_CODE
1541   // (reserved for later use)
1542   {
1543     -1,                                 -1,
1544     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1545     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1546     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1547   },
1548 #endif
1549
1550   {
1551     -1,                                 -1,
1552     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1553     &xx_ei.use_gfx_element,             FALSE,
1554     &yy_ei.use_gfx_element
1555   },
1556   {
1557     -1,                                 -1,
1558     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1559     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1560     &yy_ei.gfx_element_initial
1561   },
1562
1563   {
1564     -1,                                 -1,
1565     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1566     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1567     &yy_ei.access_direction
1568   },
1569
1570   {
1571     -1,                                 -1,
1572     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1573     &xx_ei.collect_score_initial,       10,
1574     &yy_ei.collect_score_initial
1575   },
1576   {
1577     -1,                                 -1,
1578     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1579     &xx_ei.collect_count_initial,       1,
1580     &yy_ei.collect_count_initial
1581   },
1582
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1586     &xx_ei.ce_value_fixed_initial,      0,
1587     &yy_ei.ce_value_fixed_initial
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1592     &xx_ei.ce_value_random_initial,     0,
1593     &yy_ei.ce_value_random_initial
1594   },
1595   {
1596     -1,                                 -1,
1597     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1598     &xx_ei.use_last_ce_value,           FALSE,
1599     &yy_ei.use_last_ce_value
1600   },
1601
1602   {
1603     -1,                                 -1,
1604     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1605     &xx_ei.push_delay_fixed,            8,
1606     &yy_ei.push_delay_fixed
1607   },
1608   {
1609     -1,                                 -1,
1610     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1611     &xx_ei.push_delay_random,           8,
1612     &yy_ei.push_delay_random
1613   },
1614   {
1615     -1,                                 -1,
1616     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1617     &xx_ei.drop_delay_fixed,            0,
1618     &yy_ei.drop_delay_fixed
1619   },
1620   {
1621     -1,                                 -1,
1622     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1623     &xx_ei.drop_delay_random,           0,
1624     &yy_ei.drop_delay_random
1625   },
1626   {
1627     -1,                                 -1,
1628     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1629     &xx_ei.move_delay_fixed,            0,
1630     &yy_ei.move_delay_fixed
1631   },
1632   {
1633     -1,                                 -1,
1634     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1635     &xx_ei.move_delay_random,           0,
1636     &yy_ei.move_delay_random
1637   },
1638   {
1639     -1,                                 -1,
1640     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1641     &xx_ei.step_delay_fixed,            0,
1642     &yy_ei.step_delay_fixed
1643   },
1644   {
1645     -1,                                 -1,
1646     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1647     &xx_ei.step_delay_random,           0,
1648     &yy_ei.step_delay_random
1649   },
1650
1651   {
1652     -1,                                 -1,
1653     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1654     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1655     &yy_ei.move_pattern
1656   },
1657   {
1658     -1,                                 -1,
1659     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1660     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1661     &yy_ei.move_direction_initial
1662   },
1663   {
1664     -1,                                 -1,
1665     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1666     &xx_ei.move_stepsize,               TILEX / 8,
1667     &yy_ei.move_stepsize
1668   },
1669
1670   {
1671     -1,                                 -1,
1672     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1673     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1674     &yy_ei.move_enter_element
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1679     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1680     &yy_ei.move_leave_element
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1685     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1686     &yy_ei.move_leave_type
1687   },
1688
1689   {
1690     -1,                                 -1,
1691     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1692     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1693     &yy_ei.slippery_type
1694   },
1695
1696   {
1697     -1,                                 -1,
1698     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1699     &xx_ei.explosion_type,              EXPLODES_3X3,
1700     &yy_ei.explosion_type
1701   },
1702   {
1703     -1,                                 -1,
1704     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1705     &xx_ei.explosion_delay,             16,
1706     &yy_ei.explosion_delay
1707   },
1708   {
1709     -1,                                 -1,
1710     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1711     &xx_ei.ignition_delay,              8,
1712     &yy_ei.ignition_delay
1713   },
1714
1715   {
1716     -1,                                 -1,
1717     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1718     &xx_ei.content,                     EL_EMPTY_SPACE,
1719     &yy_ei.content,
1720     &xx_num_contents,                   1, 1
1721   },
1722
1723   // ---------- "num_change_pages" must be the last entry ---------------------
1724
1725   {
1726     -1,                                 SAVE_CONF_ALWAYS,
1727     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1728     &xx_ei.num_change_pages,            1,
1729     &yy_ei.num_change_pages
1730   },
1731
1732   {
1733     -1,                                 -1,
1734     -1,                                 -1,
1735     NULL,                               -1,
1736     NULL
1737   }
1738 };
1739
1740 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1741 {
1742   // ---------- "current_change_page" must be the first entry -----------------
1743
1744   {
1745     -1,                                 SAVE_CONF_ALWAYS,
1746     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1747     &xx_current_change_page,            -1
1748   },
1749
1750   // ---------- (the remaining entries can be in any order) -------------------
1751
1752   {
1753     -1,                                 -1,
1754     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1755     &xx_change.can_change,              FALSE
1756   },
1757
1758   {
1759     -1,                                 -1,
1760     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1761     &xx_event_bits[0],                  0
1762   },
1763   {
1764     -1,                                 -1,
1765     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1766     &xx_event_bits[1],                  0
1767   },
1768
1769   {
1770     -1,                                 -1,
1771     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1772     &xx_change.trigger_player,          CH_PLAYER_ANY
1773   },
1774   {
1775     -1,                                 -1,
1776     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1777     &xx_change.trigger_side,            CH_SIDE_ANY
1778   },
1779   {
1780     -1,                                 -1,
1781     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1782     &xx_change.trigger_page,            CH_PAGE_ANY
1783   },
1784
1785   {
1786     -1,                                 -1,
1787     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1788     &xx_change.target_element,          EL_EMPTY_SPACE
1789   },
1790
1791   {
1792     -1,                                 -1,
1793     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1794     &xx_change.delay_fixed,             0
1795   },
1796   {
1797     -1,                                 -1,
1798     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1799     &xx_change.delay_random,            0
1800   },
1801   {
1802     -1,                                 -1,
1803     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1804     &xx_change.delay_frames,            FRAMES_PER_SECOND
1805   },
1806
1807   {
1808     -1,                                 -1,
1809     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1810     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1811   },
1812
1813   {
1814     -1,                                 -1,
1815     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1816     &xx_change.explode,                 FALSE
1817   },
1818   {
1819     -1,                                 -1,
1820     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1821     &xx_change.use_target_content,      FALSE
1822   },
1823   {
1824     -1,                                 -1,
1825     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1826     &xx_change.only_if_complete,        FALSE
1827   },
1828   {
1829     -1,                                 -1,
1830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1831     &xx_change.use_random_replace,      FALSE
1832   },
1833   {
1834     -1,                                 -1,
1835     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1836     &xx_change.random_percentage,       100
1837   },
1838   {
1839     -1,                                 -1,
1840     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1841     &xx_change.replace_when,            CP_WHEN_EMPTY
1842   },
1843
1844   {
1845     -1,                                 -1,
1846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1847     &xx_change.has_action,              FALSE
1848   },
1849   {
1850     -1,                                 -1,
1851     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1852     &xx_change.action_type,             CA_NO_ACTION
1853   },
1854   {
1855     -1,                                 -1,
1856     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1857     &xx_change.action_mode,             CA_MODE_UNDEFINED
1858   },
1859   {
1860     -1,                                 -1,
1861     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1862     &xx_change.action_arg,              CA_ARG_UNDEFINED
1863   },
1864
1865   {
1866     -1,                                 -1,
1867     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1868     &xx_change.action_element,          EL_EMPTY_SPACE
1869   },
1870
1871   {
1872     -1,                                 -1,
1873     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1874     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1875     &xx_num_contents,                   1, 1
1876   },
1877
1878   {
1879     -1,                                 -1,
1880     -1,                                 -1,
1881     NULL,                               -1
1882   }
1883 };
1884
1885 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1886 {
1887   {
1888     -1,                                 -1,
1889     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1890     &xx_ei.description[0],              -1, NULL,
1891     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1892     &xx_default_description[0]
1893   },
1894
1895   {
1896     -1,                                 -1,
1897     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1898     &xx_ei.use_gfx_element,             FALSE
1899   },
1900   {
1901     -1,                                 -1,
1902     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1903     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1904   },
1905
1906   {
1907     -1,                                 -1,
1908     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1909     &xx_group.choice_mode,              ANIM_RANDOM
1910   },
1911
1912   {
1913     -1,                                 -1,
1914     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1915     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1916     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1917   },
1918
1919   {
1920     -1,                                 -1,
1921     -1,                                 -1,
1922     NULL,                               -1
1923   }
1924 };
1925
1926 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1927 {
1928   {
1929     -1,                                 -1,
1930     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1931     &xx_ei.use_gfx_element,             FALSE
1932   },
1933   {
1934     -1,                                 -1,
1935     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1936     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1937   },
1938
1939   {
1940     -1,                                 -1,
1941     -1,                                 -1,
1942     NULL,                               -1
1943   }
1944 };
1945
1946 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1947 {
1948   {
1949     EL_PLAYER_1,                        -1,
1950     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1951     &li.block_snap_field,               TRUE
1952   },
1953   {
1954     EL_PLAYER_1,                        -1,
1955     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1956     &li.continuous_snapping,            TRUE
1957   },
1958   {
1959     EL_PLAYER_1,                        -1,
1960     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1961     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1962   },
1963   {
1964     EL_PLAYER_1,                        -1,
1965     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1966     &li.use_start_element[0],           FALSE
1967   },
1968   {
1969     EL_PLAYER_1,                        -1,
1970     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1971     &li.start_element[0],               EL_PLAYER_1
1972   },
1973   {
1974     EL_PLAYER_1,                        -1,
1975     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1976     &li.use_artwork_element[0],         FALSE
1977   },
1978   {
1979     EL_PLAYER_1,                        -1,
1980     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1981     &li.artwork_element[0],             EL_PLAYER_1
1982   },
1983   {
1984     EL_PLAYER_1,                        -1,
1985     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1986     &li.use_explosion_element[0],       FALSE
1987   },
1988   {
1989     EL_PLAYER_1,                        -1,
1990     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1991     &li.explosion_element[0],           EL_PLAYER_1
1992   },
1993
1994   {
1995     -1,                                 -1,
1996     -1,                                 -1,
1997     NULL,                               -1
1998   }
1999 };
2000
2001 static struct
2002 {
2003   int filetype;
2004   char *id;
2005 }
2006 filetype_id_list[] =
2007 {
2008   { LEVEL_FILE_TYPE_RND,        "RND"   },
2009   { LEVEL_FILE_TYPE_BD,         "BD"    },
2010   { LEVEL_FILE_TYPE_EM,         "EM"    },
2011   { LEVEL_FILE_TYPE_SP,         "SP"    },
2012   { LEVEL_FILE_TYPE_DX,         "DX"    },
2013   { LEVEL_FILE_TYPE_SB,         "SB"    },
2014   { LEVEL_FILE_TYPE_DC,         "DC"    },
2015   { LEVEL_FILE_TYPE_MM,         "MM"    },
2016   { LEVEL_FILE_TYPE_MM,         "DF"    },
2017   { -1,                         NULL    },
2018 };
2019
2020
2021 // ============================================================================
2022 // level file functions
2023 // ============================================================================
2024
2025 static boolean check_special_flags(char *flag)
2026 {
2027   if (strEqual(options.special_flags, flag) ||
2028       strEqual(leveldir_current->special_flags, flag))
2029     return TRUE;
2030
2031   return FALSE;
2032 }
2033
2034 static struct DateInfo getCurrentDate(void)
2035 {
2036   time_t epoch_seconds = time(NULL);
2037   struct tm *now = localtime(&epoch_seconds);
2038   struct DateInfo date;
2039
2040   date.year  = now->tm_year + 1900;
2041   date.month = now->tm_mon  + 1;
2042   date.day   = now->tm_mday;
2043
2044   date.src   = DATE_SRC_CLOCK;
2045
2046   return date;
2047 }
2048
2049 static void resetEventFlags(struct ElementChangeInfo *change)
2050 {
2051   int i;
2052
2053   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2054     change->has_event[i] = FALSE;
2055 }
2056
2057 static void resetEventBits(void)
2058 {
2059   int i;
2060
2061   for (i = 0; i < NUM_CE_BITFIELDS; i++)
2062     xx_event_bits[i] = 0;
2063 }
2064
2065 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
2066 {
2067   int i;
2068
2069   /* important: only change event flag if corresponding event bit is set
2070      (this is because all xx_event_bits[] values are loaded separately,
2071      and all xx_event_bits[] values are set back to zero before loading
2072      another value xx_event_bits[x] (each value representing 32 flags)) */
2073
2074   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2075     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
2076       change->has_event[i] = TRUE;
2077 }
2078
2079 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
2080 {
2081   int i;
2082
2083   /* in contrast to the above function setEventFlagsFromEventBits(), it
2084      would also be possible to set all bits in xx_event_bits[] to 0 or 1
2085      depending on the corresponding change->has_event[i] values here, as
2086      all xx_event_bits[] values are reset in resetEventBits() before */
2087
2088   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
2089     if (change->has_event[i])
2090       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
2091 }
2092
2093 static char *getDefaultElementDescription(struct ElementInfo *ei)
2094 {
2095   static char description[MAX_ELEMENT_NAME_LEN + 1];
2096   char *default_description = (ei->custom_description != NULL ?
2097                                ei->custom_description :
2098                                ei->editor_description);
2099   int i;
2100
2101   // always start with reliable default values
2102   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2103     description[i] = '\0';
2104
2105   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2106   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2107
2108   return &description[0];
2109 }
2110
2111 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2112 {
2113   char *default_description = getDefaultElementDescription(ei);
2114   int i;
2115
2116   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2117     ei->description[i] = default_description[i];
2118 }
2119
2120 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2121 {
2122   int i;
2123
2124   for (i = 0; conf[i].data_type != -1; i++)
2125   {
2126     int default_value = conf[i].default_value;
2127     int data_type = conf[i].data_type;
2128     int conf_type = conf[i].conf_type;
2129     int byte_mask = conf_type & CONF_MASK_BYTES;
2130
2131     if (byte_mask == CONF_MASK_MULTI_BYTES)
2132     {
2133       int default_num_entities = conf[i].default_num_entities;
2134       int max_num_entities = conf[i].max_num_entities;
2135
2136       *(int *)(conf[i].num_entities) = default_num_entities;
2137
2138       if (data_type == TYPE_STRING)
2139       {
2140         char *default_string = conf[i].default_string;
2141         char *string = (char *)(conf[i].value);
2142
2143         strncpy(string, default_string, max_num_entities);
2144       }
2145       else if (data_type == TYPE_ELEMENT_LIST)
2146       {
2147         int *element_array = (int *)(conf[i].value);
2148         int j;
2149
2150         for (j = 0; j < max_num_entities; j++)
2151           element_array[j] = default_value;
2152       }
2153       else if (data_type == TYPE_CONTENT_LIST)
2154       {
2155         struct Content *content = (struct Content *)(conf[i].value);
2156         int c, x, y;
2157
2158         for (c = 0; c < max_num_entities; c++)
2159           for (y = 0; y < 3; y++)
2160             for (x = 0; x < 3; x++)
2161               content[c].e[x][y] = default_value;
2162       }
2163     }
2164     else        // constant size configuration data (1, 2 or 4 bytes)
2165     {
2166       if (data_type == TYPE_BOOLEAN)
2167         *(boolean *)(conf[i].value) = default_value;
2168       else
2169         *(int *)    (conf[i].value) = default_value;
2170     }
2171   }
2172 }
2173
2174 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2175 {
2176   int i;
2177
2178   for (i = 0; conf[i].data_type != -1; i++)
2179   {
2180     int data_type = conf[i].data_type;
2181     int conf_type = conf[i].conf_type;
2182     int byte_mask = conf_type & CONF_MASK_BYTES;
2183
2184     if (byte_mask == CONF_MASK_MULTI_BYTES)
2185     {
2186       int max_num_entities = conf[i].max_num_entities;
2187
2188       if (data_type == TYPE_STRING)
2189       {
2190         char *string      = (char *)(conf[i].value);
2191         char *string_copy = (char *)(conf[i].value_copy);
2192
2193         strncpy(string_copy, string, max_num_entities);
2194       }
2195       else if (data_type == TYPE_ELEMENT_LIST)
2196       {
2197         int *element_array      = (int *)(conf[i].value);
2198         int *element_array_copy = (int *)(conf[i].value_copy);
2199         int j;
2200
2201         for (j = 0; j < max_num_entities; j++)
2202           element_array_copy[j] = element_array[j];
2203       }
2204       else if (data_type == TYPE_CONTENT_LIST)
2205       {
2206         struct Content *content      = (struct Content *)(conf[i].value);
2207         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2208         int c, x, y;
2209
2210         for (c = 0; c < max_num_entities; c++)
2211           for (y = 0; y < 3; y++)
2212             for (x = 0; x < 3; x++)
2213               content_copy[c].e[x][y] = content[c].e[x][y];
2214       }
2215     }
2216     else        // constant size configuration data (1, 2 or 4 bytes)
2217     {
2218       if (data_type == TYPE_BOOLEAN)
2219         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2220       else
2221         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2222     }
2223   }
2224 }
2225
2226 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2227 {
2228   int i;
2229
2230   xx_ei = *ei_from;     // copy element data into temporary buffer
2231   yy_ei = *ei_to;       // copy element data into temporary buffer
2232
2233   copyConfigFromConfigList(chunk_config_CUSX_base);
2234
2235   *ei_from = xx_ei;
2236   *ei_to   = yy_ei;
2237
2238   // ---------- reinitialize and copy change pages ----------
2239
2240   ei_to->num_change_pages = ei_from->num_change_pages;
2241   ei_to->current_change_page = ei_from->current_change_page;
2242
2243   setElementChangePages(ei_to, ei_to->num_change_pages);
2244
2245   for (i = 0; i < ei_to->num_change_pages; i++)
2246     ei_to->change_page[i] = ei_from->change_page[i];
2247
2248   // ---------- copy group element info ----------
2249   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2250     *ei_to->group = *ei_from->group;
2251
2252   // mark this custom element as modified
2253   ei_to->modified_settings = TRUE;
2254 }
2255
2256 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2257 {
2258   int change_page_size = sizeof(struct ElementChangeInfo);
2259
2260   ei->num_change_pages = MAX(1, change_pages);
2261
2262   ei->change_page =
2263     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2264
2265   if (ei->current_change_page >= ei->num_change_pages)
2266     ei->current_change_page = ei->num_change_pages - 1;
2267
2268   ei->change = &ei->change_page[ei->current_change_page];
2269 }
2270
2271 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2272 {
2273   xx_change = *change;          // copy change data into temporary buffer
2274
2275   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2276
2277   *change = xx_change;
2278
2279   resetEventFlags(change);
2280
2281   change->direct_action = 0;
2282   change->other_action = 0;
2283
2284   change->pre_change_function = NULL;
2285   change->change_function = NULL;
2286   change->post_change_function = NULL;
2287 }
2288
2289 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2290 {
2291   int i, x, y;
2292
2293   li = *level;          // copy level data into temporary buffer
2294   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2295   *level = li;          // copy temporary buffer back to level data
2296
2297   setLevelInfoToDefaults_BD();
2298   setLevelInfoToDefaults_EM();
2299   setLevelInfoToDefaults_SP();
2300   setLevelInfoToDefaults_MM();
2301
2302   level->native_bd_level = &native_bd_level;
2303   level->native_em_level = &native_em_level;
2304   level->native_sp_level = &native_sp_level;
2305   level->native_mm_level = &native_mm_level;
2306
2307   level->file_version = FILE_VERSION_ACTUAL;
2308   level->game_version = GAME_VERSION_ACTUAL;
2309
2310   level->creation_date = getCurrentDate();
2311
2312   level->encoding_16bit_field  = TRUE;
2313   level->encoding_16bit_yamyam = TRUE;
2314   level->encoding_16bit_amoeba = TRUE;
2315
2316   // clear level name and level author string buffers
2317   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2318     level->name[i] = '\0';
2319   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2320     level->author[i] = '\0';
2321
2322   // set level name and level author to default values
2323   strcpy(level->name, NAMELESS_LEVEL_NAME);
2324   strcpy(level->author, ANONYMOUS_NAME);
2325
2326   // set level playfield to playable default level with player and exit
2327   for (x = 0; x < MAX_LEV_FIELDX; x++)
2328     for (y = 0; y < MAX_LEV_FIELDY; y++)
2329       level->field[x][y] = EL_SAND;
2330
2331   level->field[0][0] = EL_PLAYER_1;
2332   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2333
2334   BorderElement = EL_STEELWALL;
2335
2336   // detect custom elements when loading them
2337   level->file_has_custom_elements = FALSE;
2338
2339   // set all bug compatibility flags to "false" => do not emulate this bug
2340   level->use_action_after_change_bug = FALSE;
2341
2342   if (leveldir_current)
2343   {
2344     // try to determine better author name than 'anonymous'
2345     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2346     {
2347       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2348       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2349     }
2350     else
2351     {
2352       switch (LEVELCLASS(leveldir_current))
2353       {
2354         case LEVELCLASS_TUTORIAL:
2355           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2356           break;
2357
2358         case LEVELCLASS_CONTRIB:
2359           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2360           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2361           break;
2362
2363         case LEVELCLASS_PRIVATE:
2364           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2365           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2366           break;
2367
2368         default:
2369           // keep default value
2370           break;
2371       }
2372     }
2373   }
2374 }
2375
2376 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2377 {
2378   static boolean clipboard_elements_initialized = FALSE;
2379   int i;
2380
2381   InitElementPropertiesStatic();
2382
2383   li = *level;          // copy level data into temporary buffer
2384   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2385   *level = li;          // copy temporary buffer back to level data
2386
2387   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2388   {
2389     int element = i;
2390     struct ElementInfo *ei = &element_info[element];
2391
2392     if (element == EL_MM_GRAY_BALL)
2393     {
2394       struct LevelInfo_MM *level_mm = level->native_mm_level;
2395       int j;
2396
2397       for (j = 0; j < level->num_mm_ball_contents; j++)
2398         level->mm_ball_content[j] =
2399           map_element_MM_to_RND(level_mm->ball_content[j]);
2400     }
2401
2402     // never initialize clipboard elements after the very first time
2403     // (to be able to use clipboard elements between several levels)
2404     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2405       continue;
2406
2407     if (IS_ENVELOPE(element))
2408     {
2409       int envelope_nr = element - EL_ENVELOPE_1;
2410
2411       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2412
2413       level->envelope[envelope_nr] = xx_envelope;
2414     }
2415
2416     if (IS_CUSTOM_ELEMENT(element) ||
2417         IS_GROUP_ELEMENT(element) ||
2418         IS_INTERNAL_ELEMENT(element))
2419     {
2420       xx_ei = *ei;      // copy element data into temporary buffer
2421
2422       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2423
2424       *ei = xx_ei;
2425     }
2426
2427     setElementChangePages(ei, 1);
2428     setElementChangeInfoToDefaults(ei->change);
2429
2430     if (IS_CUSTOM_ELEMENT(element) ||
2431         IS_GROUP_ELEMENT(element))
2432     {
2433       setElementDescriptionToDefault(ei);
2434
2435       ei->modified_settings = FALSE;
2436     }
2437
2438     if (IS_CUSTOM_ELEMENT(element) ||
2439         IS_INTERNAL_ELEMENT(element))
2440     {
2441       // internal values used in level editor
2442
2443       ei->access_type = 0;
2444       ei->access_layer = 0;
2445       ei->access_protected = 0;
2446       ei->walk_to_action = 0;
2447       ei->smash_targets = 0;
2448       ei->deadliness = 0;
2449
2450       ei->can_explode_by_fire = FALSE;
2451       ei->can_explode_smashed = FALSE;
2452       ei->can_explode_impact = FALSE;
2453
2454       ei->current_change_page = 0;
2455     }
2456
2457     if (IS_GROUP_ELEMENT(element) ||
2458         IS_INTERNAL_ELEMENT(element))
2459     {
2460       struct ElementGroupInfo *group;
2461
2462       // initialize memory for list of elements in group
2463       if (ei->group == NULL)
2464         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2465
2466       group = ei->group;
2467
2468       xx_group = *group;        // copy group data into temporary buffer
2469
2470       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2471
2472       *group = xx_group;
2473     }
2474
2475     if (IS_EMPTY_ELEMENT(element) ||
2476         IS_INTERNAL_ELEMENT(element))
2477     {
2478       xx_ei = *ei;              // copy element data into temporary buffer
2479
2480       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2481
2482       *ei = xx_ei;
2483     }
2484   }
2485
2486   clipboard_elements_initialized = TRUE;
2487 }
2488
2489 static void setLevelInfoToDefaults(struct LevelInfo *level,
2490                                    boolean level_info_only,
2491                                    boolean reset_file_status)
2492 {
2493   setLevelInfoToDefaults_Level(level);
2494
2495   if (!level_info_only)
2496     setLevelInfoToDefaults_Elements(level);
2497
2498   if (reset_file_status)
2499   {
2500     level->no_valid_file = FALSE;
2501     level->no_level_file = FALSE;
2502   }
2503
2504   level->changed = FALSE;
2505 }
2506
2507 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2508 {
2509   level_file_info->nr = 0;
2510   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2511   level_file_info->packed = FALSE;
2512
2513   setString(&level_file_info->basename, NULL);
2514   setString(&level_file_info->filename, NULL);
2515 }
2516
2517 int getMappedElement_SB(int, boolean);
2518
2519 static void ActivateLevelTemplate(void)
2520 {
2521   int x, y;
2522
2523   if (check_special_flags("load_xsb_to_ces"))
2524   {
2525     // fill smaller playfields with padding "beyond border wall" elements
2526     if (level.fieldx < level_template.fieldx ||
2527         level.fieldy < level_template.fieldy)
2528     {
2529       short field[level.fieldx][level.fieldy];
2530       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2531       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2532       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2533       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2534
2535       // copy old playfield (which is smaller than the visible area)
2536       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2537         field[x][y] = level.field[x][y];
2538
2539       // fill new, larger playfield with "beyond border wall" elements
2540       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2541         level.field[x][y] = getMappedElement_SB('_', TRUE);
2542
2543       // copy the old playfield to the middle of the new playfield
2544       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2545         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2546
2547       level.fieldx = new_fieldx;
2548       level.fieldy = new_fieldy;
2549     }
2550   }
2551
2552   // Currently there is no special action needed to activate the template
2553   // data, because 'element_info' property settings overwrite the original
2554   // level data, while all other variables do not change.
2555
2556   // Exception: 'from_level_template' elements in the original level playfield
2557   // are overwritten with the corresponding elements at the same position in
2558   // playfield from the level template.
2559
2560   for (x = 0; x < level.fieldx; x++)
2561     for (y = 0; y < level.fieldy; y++)
2562       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2563         level.field[x][y] = level_template.field[x][y];
2564
2565   if (check_special_flags("load_xsb_to_ces"))
2566   {
2567     struct LevelInfo level_backup = level;
2568
2569     // overwrite all individual level settings from template level settings
2570     level = level_template;
2571
2572     // restore level file info
2573     level.file_info = level_backup.file_info;
2574
2575     // restore playfield size
2576     level.fieldx = level_backup.fieldx;
2577     level.fieldy = level_backup.fieldy;
2578
2579     // restore playfield content
2580     for (x = 0; x < level.fieldx; x++)
2581       for (y = 0; y < level.fieldy; y++)
2582         level.field[x][y] = level_backup.field[x][y];
2583
2584     // restore name and author from individual level
2585     strcpy(level.name,   level_backup.name);
2586     strcpy(level.author, level_backup.author);
2587
2588     // restore flag "use_custom_template"
2589     level.use_custom_template = level_backup.use_custom_template;
2590   }
2591 }
2592
2593 static boolean checkForPackageFromBasename_BD(char *basename)
2594 {
2595   // check for native BD level file extensions
2596   if (!strSuffixLower(basename, ".bd") &&
2597       !strSuffixLower(basename, ".bdr") &&
2598       !strSuffixLower(basename, ".brc") &&
2599       !strSuffixLower(basename, ".gds"))
2600     return FALSE;
2601
2602   // check for standard single-level BD files (like "001.bd")
2603   if (strSuffixLower(basename, ".bd") &&
2604       strlen(basename) == 6 &&
2605       basename[0] >= '0' && basename[0] <= '9' &&
2606       basename[1] >= '0' && basename[1] <= '9' &&
2607       basename[2] >= '0' && basename[2] <= '9')
2608     return FALSE;
2609
2610   // this is a level package in native BD file format
2611   return TRUE;
2612 }
2613
2614 static char *getLevelFilenameFromBasename(char *basename)
2615 {
2616   static char *filename = NULL;
2617
2618   checked_free(filename);
2619
2620   filename = getPath2(getCurrentLevelDir(), basename);
2621
2622   return filename;
2623 }
2624
2625 static int getFileTypeFromBasename(char *basename)
2626 {
2627   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2628
2629   static char *filename = NULL;
2630   struct stat file_status;
2631
2632   // ---------- try to determine file type from filename ----------
2633
2634   // check for typical filename of a Supaplex level package file
2635   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2636     return LEVEL_FILE_TYPE_SP;
2637
2638   // check for typical filename of a Diamond Caves II level package file
2639   if (strSuffixLower(basename, ".dc") ||
2640       strSuffixLower(basename, ".dc2"))
2641     return LEVEL_FILE_TYPE_DC;
2642
2643   // check for typical filename of a Sokoban level package file
2644   if (strSuffixLower(basename, ".xsb") &&
2645       strchr(basename, '%') == NULL)
2646     return LEVEL_FILE_TYPE_SB;
2647
2648   // check for typical filename of a Boulder Dash (GDash) level package file
2649   if (checkForPackageFromBasename_BD(basename))
2650     return LEVEL_FILE_TYPE_BD;
2651
2652   // ---------- try to determine file type from filesize ----------
2653
2654   checked_free(filename);
2655   filename = getPath2(getCurrentLevelDir(), basename);
2656
2657   if (stat(filename, &file_status) == 0)
2658   {
2659     // check for typical filesize of a Supaplex level package file
2660     if (file_status.st_size == 170496)
2661       return LEVEL_FILE_TYPE_SP;
2662   }
2663
2664   return LEVEL_FILE_TYPE_UNKNOWN;
2665 }
2666
2667 static int getFileTypeFromMagicBytes(char *filename, int type)
2668 {
2669   File *file;
2670
2671   if ((file = openFile(filename, MODE_READ)))
2672   {
2673     char chunk_name[CHUNK_ID_LEN + 1];
2674
2675     getFileChunkBE(file, chunk_name, NULL);
2676
2677     if (strEqual(chunk_name, "MMII") ||
2678         strEqual(chunk_name, "MIRR"))
2679       type = LEVEL_FILE_TYPE_MM;
2680
2681     closeFile(file);
2682   }
2683
2684   return type;
2685 }
2686
2687 static boolean checkForPackageFromBasename(char *basename)
2688 {
2689   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2690   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2691
2692   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2693 }
2694
2695 static char *getSingleLevelBasenameExt(int nr, char *extension)
2696 {
2697   static char basename[MAX_FILENAME_LEN];
2698
2699   if (nr < 0)
2700     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2701   else
2702     sprintf(basename, "%03d.%s", nr, extension);
2703
2704   return basename;
2705 }
2706
2707 static char *getSingleLevelBasename(int nr)
2708 {
2709   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2710 }
2711
2712 static char *getPackedLevelBasename(int type)
2713 {
2714   static char basename[MAX_FILENAME_LEN];
2715   char *directory = getCurrentLevelDir();
2716   Directory *dir;
2717   DirectoryEntry *dir_entry;
2718
2719   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2720
2721   if ((dir = openDirectory(directory)) == NULL)
2722   {
2723     Warn("cannot read current level directory '%s'", directory);
2724
2725     return basename;
2726   }
2727
2728   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2729   {
2730     char *entry_basename = dir_entry->basename;
2731     int entry_type = getFileTypeFromBasename(entry_basename);
2732
2733     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2734     {
2735       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2736           type == entry_type)
2737       {
2738         strcpy(basename, entry_basename);
2739
2740         break;
2741       }
2742     }
2743   }
2744
2745   closeDirectory(dir);
2746
2747   return basename;
2748 }
2749
2750 static char *getSingleLevelFilename(int nr)
2751 {
2752   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2753 }
2754
2755 #if ENABLE_UNUSED_CODE
2756 static char *getPackedLevelFilename(int type)
2757 {
2758   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2759 }
2760 #endif
2761
2762 char *getDefaultLevelFilename(int nr)
2763 {
2764   return getSingleLevelFilename(nr);
2765 }
2766
2767 #if ENABLE_UNUSED_CODE
2768 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2769                                                  int type)
2770 {
2771   lfi->type = type;
2772   lfi->packed = FALSE;
2773
2774   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2775   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2776 }
2777 #endif
2778
2779 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2780                                                  int type, char *format, ...)
2781 {
2782   static char basename[MAX_FILENAME_LEN];
2783   va_list ap;
2784
2785   va_start(ap, format);
2786   vsprintf(basename, format, ap);
2787   va_end(ap);
2788
2789   lfi->type = type;
2790   lfi->packed = FALSE;
2791
2792   setString(&lfi->basename, basename);
2793   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2794 }
2795
2796 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2797                                                  int type)
2798 {
2799   lfi->type = type;
2800   lfi->packed = TRUE;
2801
2802   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2803   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2804 }
2805
2806 static int getFiletypeFromID(char *filetype_id)
2807 {
2808   char *filetype_id_lower;
2809   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2810   int i;
2811
2812   if (filetype_id == NULL)
2813     return LEVEL_FILE_TYPE_UNKNOWN;
2814
2815   filetype_id_lower = getStringToLower(filetype_id);
2816
2817   for (i = 0; filetype_id_list[i].id != NULL; i++)
2818   {
2819     char *id_lower = getStringToLower(filetype_id_list[i].id);
2820     
2821     if (strEqual(filetype_id_lower, id_lower))
2822       filetype = filetype_id_list[i].filetype;
2823
2824     free(id_lower);
2825
2826     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2827       break;
2828   }
2829
2830   free(filetype_id_lower);
2831
2832   return filetype;
2833 }
2834
2835 char *getLocalLevelTemplateFilename(void)
2836 {
2837   return getDefaultLevelFilename(-1);
2838 }
2839
2840 char *getGlobalLevelTemplateFilename(void)
2841 {
2842   // global variable "leveldir_current" must be modified in the loop below
2843   LevelDirTree *leveldir_current_last = leveldir_current;
2844   char *filename = NULL;
2845
2846   // check for template level in path from current to topmost tree node
2847
2848   while (leveldir_current != NULL)
2849   {
2850     filename = getDefaultLevelFilename(-1);
2851
2852     if (fileExists(filename))
2853       break;
2854
2855     leveldir_current = leveldir_current->node_parent;
2856   }
2857
2858   // restore global variable "leveldir_current" modified in above loop
2859   leveldir_current = leveldir_current_last;
2860
2861   return filename;
2862 }
2863
2864 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2865 {
2866   int nr = lfi->nr;
2867
2868   // special case: level number is negative => check for level template file
2869   if (nr < 0)
2870   {
2871     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2872                                          getSingleLevelBasename(-1));
2873
2874     // replace local level template filename with global template filename
2875     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2876
2877     // no fallback if template file not existing
2878     return;
2879   }
2880
2881   // special case: check for file name/pattern specified in "levelinfo.conf"
2882   if (leveldir_current->level_filename != NULL)
2883   {
2884     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2885
2886     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2887                                          leveldir_current->level_filename, nr);
2888
2889     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2890
2891     if (fileExists(lfi->filename))
2892       return;
2893   }
2894   else if (leveldir_current->level_filetype != NULL)
2895   {
2896     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2897
2898     // check for specified native level file with standard file name
2899     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2900                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2901     if (fileExists(lfi->filename))
2902       return;
2903   }
2904
2905   // check for native Rocks'n'Diamonds level file
2906   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2907                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2908   if (fileExists(lfi->filename))
2909     return;
2910
2911   // check for native Boulder Dash level file
2912   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2913   if (fileExists(lfi->filename))
2914     return;
2915
2916   // check for Emerald Mine level file (V1)
2917   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2918                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2919   if (fileExists(lfi->filename))
2920     return;
2921   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2922                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2923   if (fileExists(lfi->filename))
2924     return;
2925
2926   // check for Emerald Mine level file (V2 to V5)
2927   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2928   if (fileExists(lfi->filename))
2929     return;
2930
2931   // check for Emerald Mine level file (V6 / single mode)
2932   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2933   if (fileExists(lfi->filename))
2934     return;
2935   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2936   if (fileExists(lfi->filename))
2937     return;
2938
2939   // check for Emerald Mine level file (V6 / teamwork mode)
2940   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2941   if (fileExists(lfi->filename))
2942     return;
2943   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2944   if (fileExists(lfi->filename))
2945     return;
2946
2947   // check for various packed level file formats
2948   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2949   if (fileExists(lfi->filename))
2950     return;
2951
2952   // no known level file found -- use default values (and fail later)
2953   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2954                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2955 }
2956
2957 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2958 {
2959   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2960     lfi->type = getFileTypeFromBasename(lfi->basename);
2961
2962   if (lfi->type == LEVEL_FILE_TYPE_RND)
2963     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2964 }
2965
2966 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2967 {
2968   // always start with reliable default values
2969   setFileInfoToDefaults(level_file_info);
2970
2971   level_file_info->nr = nr;     // set requested level number
2972
2973   determineLevelFileInfo_Filename(level_file_info);
2974   determineLevelFileInfo_Filetype(level_file_info);
2975 }
2976
2977 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2978                               struct LevelFileInfo *lfi_to)
2979 {
2980   lfi_to->nr     = lfi_from->nr;
2981   lfi_to->type   = lfi_from->type;
2982   lfi_to->packed = lfi_from->packed;
2983
2984   setString(&lfi_to->basename, lfi_from->basename);
2985   setString(&lfi_to->filename, lfi_from->filename);
2986 }
2987
2988 // ----------------------------------------------------------------------------
2989 // functions for loading R'n'D level
2990 // ----------------------------------------------------------------------------
2991
2992 int getMappedElement(int element)
2993 {
2994   // remap some (historic, now obsolete) elements
2995
2996   switch (element)
2997   {
2998     case EL_PLAYER_OBSOLETE:
2999       element = EL_PLAYER_1;
3000       break;
3001
3002     case EL_KEY_OBSOLETE:
3003       element = EL_KEY_1;
3004       break;
3005
3006     case EL_EM_KEY_1_FILE_OBSOLETE:
3007       element = EL_EM_KEY_1;
3008       break;
3009
3010     case EL_EM_KEY_2_FILE_OBSOLETE:
3011       element = EL_EM_KEY_2;
3012       break;
3013
3014     case EL_EM_KEY_3_FILE_OBSOLETE:
3015       element = EL_EM_KEY_3;
3016       break;
3017
3018     case EL_EM_KEY_4_FILE_OBSOLETE:
3019       element = EL_EM_KEY_4;
3020       break;
3021
3022     case EL_ENVELOPE_OBSOLETE:
3023       element = EL_ENVELOPE_1;
3024       break;
3025
3026     case EL_SP_EMPTY:
3027       element = EL_EMPTY;
3028       break;
3029
3030     default:
3031       if (element >= NUM_FILE_ELEMENTS)
3032       {
3033         Warn("invalid level element %d", element);
3034
3035         element = EL_UNKNOWN;
3036       }
3037       break;
3038   }
3039
3040   return element;
3041 }
3042
3043 static int getMappedElementByVersion(int element, int game_version)
3044 {
3045   // remap some elements due to certain game version
3046
3047   if (game_version <= VERSION_IDENT(2,2,0,0))
3048   {
3049     // map game font elements
3050     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
3051                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
3052                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
3053                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
3054   }
3055
3056   if (game_version < VERSION_IDENT(3,0,0,0))
3057   {
3058     // map Supaplex gravity tube elements
3059     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
3060                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
3061                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
3062                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
3063                element);
3064   }
3065
3066   return element;
3067 }
3068
3069 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
3070 {
3071   level->file_version = getFileVersion(file);
3072   level->game_version = getFileVersion(file);
3073
3074   return chunk_size;
3075 }
3076
3077 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
3078 {
3079   level->creation_date.year  = getFile16BitBE(file);
3080   level->creation_date.month = getFile8Bit(file);
3081   level->creation_date.day   = getFile8Bit(file);
3082
3083   level->creation_date.src   = DATE_SRC_LEVELFILE;
3084
3085   return chunk_size;
3086 }
3087
3088 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
3089 {
3090   int initial_player_stepsize;
3091   int initial_player_gravity;
3092   int i, x, y;
3093
3094   level->fieldx = getFile8Bit(file);
3095   level->fieldy = getFile8Bit(file);
3096
3097   level->time           = getFile16BitBE(file);
3098   level->gems_needed    = getFile16BitBE(file);
3099
3100   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3101     level->name[i] = getFile8Bit(file);
3102   level->name[MAX_LEVEL_NAME_LEN] = 0;
3103
3104   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3105     level->score[i] = getFile8Bit(file);
3106
3107   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3108   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3109     for (y = 0; y < 3; y++)
3110       for (x = 0; x < 3; x++)
3111         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3112
3113   level->amoeba_speed           = getFile8Bit(file);
3114   level->time_magic_wall        = getFile8Bit(file);
3115   level->time_wheel             = getFile8Bit(file);
3116   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3117
3118   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3119                                    STEPSIZE_NORMAL);
3120
3121   for (i = 0; i < MAX_PLAYERS; i++)
3122     level->initial_player_stepsize[i] = initial_player_stepsize;
3123
3124   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3125
3126   for (i = 0; i < MAX_PLAYERS; i++)
3127     level->initial_player_gravity[i] = initial_player_gravity;
3128
3129   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3130   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3131
3132   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3133
3134   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3135   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3136   level->can_move_into_acid_bits = getFile32BitBE(file);
3137   level->dont_collide_with_bits = getFile8Bit(file);
3138
3139   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3140   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3141
3142   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3143   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3144   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3145
3146   level->game_engine_type       = getFile8Bit(file);
3147
3148   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3149
3150   return chunk_size;
3151 }
3152
3153 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3154 {
3155   int i;
3156
3157   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3158     level->name[i] = getFile8Bit(file);
3159   level->name[MAX_LEVEL_NAME_LEN] = 0;
3160
3161   return chunk_size;
3162 }
3163
3164 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3165 {
3166   int i;
3167
3168   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3169     level->author[i] = getFile8Bit(file);
3170   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3171
3172   return chunk_size;
3173 }
3174
3175 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3176 {
3177   int x, y;
3178   int chunk_size_expected = level->fieldx * level->fieldy;
3179
3180   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3181      stored with 16-bit encoding (and should be twice as big then).
3182      Even worse, playfield data was stored 16-bit when only yamyam content
3183      contained 16-bit elements and vice versa. */
3184
3185   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3186     chunk_size_expected *= 2;
3187
3188   if (chunk_size_expected != chunk_size)
3189   {
3190     ReadUnusedBytesFromFile(file, chunk_size);
3191     return chunk_size_expected;
3192   }
3193
3194   for (y = 0; y < level->fieldy; y++)
3195     for (x = 0; x < level->fieldx; x++)
3196       level->field[x][y] =
3197         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3198                          getFile8Bit(file));
3199   return chunk_size;
3200 }
3201
3202 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3203 {
3204   int i, x, y;
3205   int header_size = 4;
3206   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3207   int chunk_size_expected = header_size + content_size;
3208
3209   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3210      stored with 16-bit encoding (and should be twice as big then).
3211      Even worse, playfield data was stored 16-bit when only yamyam content
3212      contained 16-bit elements and vice versa. */
3213
3214   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3215     chunk_size_expected += content_size;
3216
3217   if (chunk_size_expected != chunk_size)
3218   {
3219     ReadUnusedBytesFromFile(file, chunk_size);
3220     return chunk_size_expected;
3221   }
3222
3223   getFile8Bit(file);
3224   level->num_yamyam_contents = getFile8Bit(file);
3225   getFile8Bit(file);
3226   getFile8Bit(file);
3227
3228   // correct invalid number of content fields -- should never happen
3229   if (level->num_yamyam_contents < 1 ||
3230       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3231     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3232
3233   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3234     for (y = 0; y < 3; y++)
3235       for (x = 0; x < 3; x++)
3236         level->yamyam_content[i].e[x][y] =
3237           getMappedElement(level->encoding_16bit_field ?
3238                            getFile16BitBE(file) : getFile8Bit(file));
3239   return chunk_size;
3240 }
3241
3242 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3243 {
3244   int i, x, y;
3245   int element;
3246   int num_contents;
3247   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3248
3249   element = getMappedElement(getFile16BitBE(file));
3250   num_contents = getFile8Bit(file);
3251
3252   getFile8Bit(file);    // content x size (unused)
3253   getFile8Bit(file);    // content y size (unused)
3254
3255   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3256
3257   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3258     for (y = 0; y < 3; y++)
3259       for (x = 0; x < 3; x++)
3260         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3261
3262   // correct invalid number of content fields -- should never happen
3263   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3264     num_contents = STD_ELEMENT_CONTENTS;
3265
3266   if (element == EL_YAMYAM)
3267   {
3268     level->num_yamyam_contents = num_contents;
3269
3270     for (i = 0; i < num_contents; i++)
3271       for (y = 0; y < 3; y++)
3272         for (x = 0; x < 3; x++)
3273           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3274   }
3275   else if (element == EL_BD_AMOEBA)
3276   {
3277     level->amoeba_content = content_array[0][0][0];
3278   }
3279   else
3280   {
3281     Warn("cannot load content for element '%d'", element);
3282   }
3283
3284   return chunk_size;
3285 }
3286
3287 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3288 {
3289   int i;
3290   int element;
3291   int envelope_nr;
3292   int envelope_len;
3293   int chunk_size_expected;
3294
3295   element = getMappedElement(getFile16BitBE(file));
3296   if (!IS_ENVELOPE(element))
3297     element = EL_ENVELOPE_1;
3298
3299   envelope_nr = element - EL_ENVELOPE_1;
3300
3301   envelope_len = getFile16BitBE(file);
3302
3303   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3304   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3305
3306   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3307
3308   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3309   if (chunk_size_expected != chunk_size)
3310   {
3311     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3312     return chunk_size_expected;
3313   }
3314
3315   for (i = 0; i < envelope_len; i++)
3316     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3317
3318   return chunk_size;
3319 }
3320
3321 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3322 {
3323   int num_changed_custom_elements = getFile16BitBE(file);
3324   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3325   int i;
3326
3327   if (chunk_size_expected != chunk_size)
3328   {
3329     ReadUnusedBytesFromFile(file, chunk_size - 2);
3330     return chunk_size_expected;
3331   }
3332
3333   for (i = 0; i < num_changed_custom_elements; i++)
3334   {
3335     int element = getMappedElement(getFile16BitBE(file));
3336     int properties = getFile32BitBE(file);
3337
3338     if (IS_CUSTOM_ELEMENT(element))
3339       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3340     else
3341       Warn("invalid custom element number %d", element);
3342
3343     // older game versions that wrote level files with CUS1 chunks used
3344     // different default push delay values (not yet stored in level file)
3345     element_info[element].push_delay_fixed = 2;
3346     element_info[element].push_delay_random = 8;
3347   }
3348
3349   level->file_has_custom_elements = TRUE;
3350
3351   return chunk_size;
3352 }
3353
3354 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3355 {
3356   int num_changed_custom_elements = getFile16BitBE(file);
3357   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3358   int i;
3359
3360   if (chunk_size_expected != chunk_size)
3361   {
3362     ReadUnusedBytesFromFile(file, chunk_size - 2);
3363     return chunk_size_expected;
3364   }
3365
3366   for (i = 0; i < num_changed_custom_elements; i++)
3367   {
3368     int element = getMappedElement(getFile16BitBE(file));
3369     int custom_target_element = getMappedElement(getFile16BitBE(file));
3370
3371     if (IS_CUSTOM_ELEMENT(element))
3372       element_info[element].change->target_element = custom_target_element;
3373     else
3374       Warn("invalid custom element number %d", element);
3375   }
3376
3377   level->file_has_custom_elements = TRUE;
3378
3379   return chunk_size;
3380 }
3381
3382 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3383 {
3384   int num_changed_custom_elements = getFile16BitBE(file);
3385   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3386   int i, j, x, y;
3387
3388   if (chunk_size_expected != chunk_size)
3389   {
3390     ReadUnusedBytesFromFile(file, chunk_size - 2);
3391     return chunk_size_expected;
3392   }
3393
3394   for (i = 0; i < num_changed_custom_elements; i++)
3395   {
3396     int element = getMappedElement(getFile16BitBE(file));
3397     struct ElementInfo *ei = &element_info[element];
3398     unsigned int event_bits;
3399
3400     if (!IS_CUSTOM_ELEMENT(element))
3401     {
3402       Warn("invalid custom element number %d", element);
3403
3404       element = EL_INTERNAL_DUMMY;
3405     }
3406
3407     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3408       ei->description[j] = getFile8Bit(file);
3409     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3410
3411     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3412
3413     // some free bytes for future properties and padding
3414     ReadUnusedBytesFromFile(file, 7);
3415
3416     ei->use_gfx_element = getFile8Bit(file);
3417     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3418
3419     ei->collect_score_initial = getFile8Bit(file);
3420     ei->collect_count_initial = getFile8Bit(file);
3421
3422     ei->push_delay_fixed = getFile16BitBE(file);
3423     ei->push_delay_random = getFile16BitBE(file);
3424     ei->move_delay_fixed = getFile16BitBE(file);
3425     ei->move_delay_random = getFile16BitBE(file);
3426
3427     ei->move_pattern = getFile16BitBE(file);
3428     ei->move_direction_initial = getFile8Bit(file);
3429     ei->move_stepsize = getFile8Bit(file);
3430
3431     for (y = 0; y < 3; y++)
3432       for (x = 0; x < 3; x++)
3433         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3434
3435     // bits 0 - 31 of "has_event[]"
3436     event_bits = getFile32BitBE(file);
3437     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3438       if (event_bits & (1u << j))
3439         ei->change->has_event[j] = TRUE;
3440
3441     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3442
3443     ei->change->delay_fixed = getFile16BitBE(file);
3444     ei->change->delay_random = getFile16BitBE(file);
3445     ei->change->delay_frames = getFile16BitBE(file);
3446
3447     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3448
3449     ei->change->explode = getFile8Bit(file);
3450     ei->change->use_target_content = getFile8Bit(file);
3451     ei->change->only_if_complete = getFile8Bit(file);
3452     ei->change->use_random_replace = getFile8Bit(file);
3453
3454     ei->change->random_percentage = getFile8Bit(file);
3455     ei->change->replace_when = getFile8Bit(file);
3456
3457     for (y = 0; y < 3; y++)
3458       for (x = 0; x < 3; x++)
3459         ei->change->target_content.e[x][y] =
3460           getMappedElement(getFile16BitBE(file));
3461
3462     ei->slippery_type = getFile8Bit(file);
3463
3464     // some free bytes for future properties and padding
3465     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3466
3467     // mark that this custom element has been modified
3468     ei->modified_settings = TRUE;
3469   }
3470
3471   level->file_has_custom_elements = TRUE;
3472
3473   return chunk_size;
3474 }
3475
3476 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3477 {
3478   struct ElementInfo *ei;
3479   int chunk_size_expected;
3480   int element;
3481   int i, j, x, y;
3482
3483   // ---------- custom element base property values (96 bytes) ----------------
3484
3485   element = getMappedElement(getFile16BitBE(file));
3486
3487   if (!IS_CUSTOM_ELEMENT(element))
3488   {
3489     Warn("invalid custom element number %d", element);
3490
3491     ReadUnusedBytesFromFile(file, chunk_size - 2);
3492
3493     return chunk_size;
3494   }
3495
3496   ei = &element_info[element];
3497
3498   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3499     ei->description[i] = getFile8Bit(file);
3500   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3501
3502   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3503
3504   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3505
3506   ei->num_change_pages = getFile8Bit(file);
3507
3508   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3509   if (chunk_size_expected != chunk_size)
3510   {
3511     ReadUnusedBytesFromFile(file, chunk_size - 43);
3512     return chunk_size_expected;
3513   }
3514
3515   ei->ce_value_fixed_initial = getFile16BitBE(file);
3516   ei->ce_value_random_initial = getFile16BitBE(file);
3517   ei->use_last_ce_value = getFile8Bit(file);
3518
3519   ei->use_gfx_element = getFile8Bit(file);
3520   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3521
3522   ei->collect_score_initial = getFile8Bit(file);
3523   ei->collect_count_initial = getFile8Bit(file);
3524
3525   ei->drop_delay_fixed = getFile8Bit(file);
3526   ei->push_delay_fixed = getFile8Bit(file);
3527   ei->drop_delay_random = getFile8Bit(file);
3528   ei->push_delay_random = getFile8Bit(file);
3529   ei->move_delay_fixed = getFile16BitBE(file);
3530   ei->move_delay_random = getFile16BitBE(file);
3531
3532   // bits 0 - 15 of "move_pattern" ...
3533   ei->move_pattern = getFile16BitBE(file);
3534   ei->move_direction_initial = getFile8Bit(file);
3535   ei->move_stepsize = getFile8Bit(file);
3536
3537   ei->slippery_type = getFile8Bit(file);
3538
3539   for (y = 0; y < 3; y++)
3540     for (x = 0; x < 3; x++)
3541       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3542
3543   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3544   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3545   ei->move_leave_type = getFile8Bit(file);
3546
3547   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3548   ei->move_pattern |= (getFile16BitBE(file) << 16);
3549
3550   ei->access_direction = getFile8Bit(file);
3551
3552   ei->explosion_delay = getFile8Bit(file);
3553   ei->ignition_delay = getFile8Bit(file);
3554   ei->explosion_type = getFile8Bit(file);
3555
3556   // some free bytes for future custom property values and padding
3557   ReadUnusedBytesFromFile(file, 1);
3558
3559   // ---------- change page property values (48 bytes) ------------------------
3560
3561   setElementChangePages(ei, ei->num_change_pages);
3562
3563   for (i = 0; i < ei->num_change_pages; i++)
3564   {
3565     struct ElementChangeInfo *change = &ei->change_page[i];
3566     unsigned int event_bits;
3567
3568     // always start with reliable default values
3569     setElementChangeInfoToDefaults(change);
3570
3571     // bits 0 - 31 of "has_event[]" ...
3572     event_bits = getFile32BitBE(file);
3573     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3574       if (event_bits & (1u << j))
3575         change->has_event[j] = TRUE;
3576
3577     change->target_element = getMappedElement(getFile16BitBE(file));
3578
3579     change->delay_fixed = getFile16BitBE(file);
3580     change->delay_random = getFile16BitBE(file);
3581     change->delay_frames = getFile16BitBE(file);
3582
3583     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3584
3585     change->explode = getFile8Bit(file);
3586     change->use_target_content = getFile8Bit(file);
3587     change->only_if_complete = getFile8Bit(file);
3588     change->use_random_replace = getFile8Bit(file);
3589
3590     change->random_percentage = getFile8Bit(file);
3591     change->replace_when = getFile8Bit(file);
3592
3593     for (y = 0; y < 3; y++)
3594       for (x = 0; x < 3; x++)
3595         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3596
3597     change->can_change = getFile8Bit(file);
3598
3599     change->trigger_side = getFile8Bit(file);
3600
3601     change->trigger_player = getFile8Bit(file);
3602     change->trigger_page = getFile8Bit(file);
3603
3604     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3605                             CH_PAGE_ANY : (1 << change->trigger_page));
3606
3607     change->has_action = getFile8Bit(file);
3608     change->action_type = getFile8Bit(file);
3609     change->action_mode = getFile8Bit(file);
3610     change->action_arg = getFile16BitBE(file);
3611
3612     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3613     event_bits = getFile8Bit(file);
3614     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3615       if (event_bits & (1u << (j - 32)))
3616         change->has_event[j] = TRUE;
3617   }
3618
3619   // mark this custom element as modified
3620   ei->modified_settings = TRUE;
3621
3622   level->file_has_custom_elements = TRUE;
3623
3624   return chunk_size;
3625 }
3626
3627 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3628 {
3629   struct ElementInfo *ei;
3630   struct ElementGroupInfo *group;
3631   int element;
3632   int i;
3633
3634   element = getMappedElement(getFile16BitBE(file));
3635
3636   if (!IS_GROUP_ELEMENT(element))
3637   {
3638     Warn("invalid group element number %d", element);
3639
3640     ReadUnusedBytesFromFile(file, chunk_size - 2);
3641
3642     return chunk_size;
3643   }
3644
3645   ei = &element_info[element];
3646
3647   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3648     ei->description[i] = getFile8Bit(file);
3649   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3650
3651   group = element_info[element].group;
3652
3653   group->num_elements = getFile8Bit(file);
3654
3655   ei->use_gfx_element = getFile8Bit(file);
3656   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3657
3658   group->choice_mode = getFile8Bit(file);
3659
3660   // some free bytes for future values and padding
3661   ReadUnusedBytesFromFile(file, 3);
3662
3663   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3664     group->element[i] = getMappedElement(getFile16BitBE(file));
3665
3666   // mark this group element as modified
3667   element_info[element].modified_settings = TRUE;
3668
3669   level->file_has_custom_elements = TRUE;
3670
3671   return chunk_size;
3672 }
3673
3674 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3675                                 int element, int real_element)
3676 {
3677   int micro_chunk_size = 0;
3678   int conf_type = getFile8Bit(file);
3679   int byte_mask = conf_type & CONF_MASK_BYTES;
3680   boolean element_found = FALSE;
3681   int i;
3682
3683   micro_chunk_size += 1;
3684
3685   if (byte_mask == CONF_MASK_MULTI_BYTES)
3686   {
3687     int num_bytes = getFile16BitBE(file);
3688     byte *buffer = checked_malloc(num_bytes);
3689
3690     ReadBytesFromFile(file, buffer, num_bytes);
3691
3692     for (i = 0; conf[i].data_type != -1; i++)
3693     {
3694       if (conf[i].element == element &&
3695           conf[i].conf_type == conf_type)
3696       {
3697         int data_type = conf[i].data_type;
3698         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3699         int max_num_entities = conf[i].max_num_entities;
3700
3701         if (num_entities > max_num_entities)
3702         {
3703           Warn("truncating number of entities for element %d from %d to %d",
3704                element, num_entities, max_num_entities);
3705
3706           num_entities = max_num_entities;
3707         }
3708
3709         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3710                                   data_type == TYPE_CONTENT_LIST))
3711         {
3712           // for element and content lists, zero entities are not allowed
3713           Warn("found empty list of entities for element %d", element);
3714
3715           // do not set "num_entities" here to prevent reading behind buffer
3716
3717           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3718         }
3719         else
3720         {
3721           *(int *)(conf[i].num_entities) = num_entities;
3722         }
3723
3724         element_found = TRUE;
3725
3726         if (data_type == TYPE_STRING)
3727         {
3728           char *string = (char *)(conf[i].value);
3729           int j;
3730
3731           for (j = 0; j < max_num_entities; j++)
3732             string[j] = (j < num_entities ? buffer[j] : '\0');
3733         }
3734         else if (data_type == TYPE_ELEMENT_LIST)
3735         {
3736           int *element_array = (int *)(conf[i].value);
3737           int j;
3738
3739           for (j = 0; j < num_entities; j++)
3740             element_array[j] =
3741               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3742         }
3743         else if (data_type == TYPE_CONTENT_LIST)
3744         {
3745           struct Content *content= (struct Content *)(conf[i].value);
3746           int c, x, y;
3747
3748           for (c = 0; c < num_entities; c++)
3749             for (y = 0; y < 3; y++)
3750               for (x = 0; x < 3; x++)
3751                 content[c].e[x][y] =
3752                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3753         }
3754         else
3755           element_found = FALSE;
3756
3757         break;
3758       }
3759     }
3760
3761     checked_free(buffer);
3762
3763     micro_chunk_size += 2 + num_bytes;
3764   }
3765   else          // constant size configuration data (1, 2 or 4 bytes)
3766   {
3767     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3768                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3769                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3770
3771     for (i = 0; conf[i].data_type != -1; i++)
3772     {
3773       if (conf[i].element == element &&
3774           conf[i].conf_type == conf_type)
3775       {
3776         int data_type = conf[i].data_type;
3777
3778         if (data_type == TYPE_ELEMENT)
3779           value = getMappedElement(value);
3780
3781         if (data_type == TYPE_BOOLEAN)
3782           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3783         else
3784           *(int *)    (conf[i].value) = value;
3785
3786         element_found = TRUE;
3787
3788         break;
3789       }
3790     }
3791
3792     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3793   }
3794
3795   if (!element_found)
3796   {
3797     char *error_conf_chunk_bytes =
3798       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3799        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3800        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3801     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3802     int error_element = real_element;
3803
3804     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3805          error_conf_chunk_bytes, error_conf_chunk_token,
3806          error_element, EL_NAME(error_element));
3807   }
3808
3809   return micro_chunk_size;
3810 }
3811
3812 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3813 {
3814   int real_chunk_size = 0;
3815
3816   li = *level;          // copy level data into temporary buffer
3817
3818   while (!checkEndOfFile(file))
3819   {
3820     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3821
3822     if (real_chunk_size >= chunk_size)
3823       break;
3824   }
3825
3826   *level = li;          // copy temporary buffer back to level data
3827
3828   return real_chunk_size;
3829 }
3830
3831 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3832 {
3833   int real_chunk_size = 0;
3834
3835   li = *level;          // copy level data into temporary buffer
3836
3837   while (!checkEndOfFile(file))
3838   {
3839     int element = getMappedElement(getFile16BitBE(file));
3840
3841     real_chunk_size += 2;
3842     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3843                                             element, element);
3844     if (real_chunk_size >= chunk_size)
3845       break;
3846   }
3847
3848   *level = li;          // copy temporary buffer back to level data
3849
3850   return real_chunk_size;
3851 }
3852
3853 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3854 {
3855   int real_chunk_size = 0;
3856
3857   li = *level;          // copy level data into temporary buffer
3858
3859   while (!checkEndOfFile(file))
3860   {
3861     int element = getMappedElement(getFile16BitBE(file));
3862
3863     real_chunk_size += 2;
3864     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3865                                             element, element);
3866     if (real_chunk_size >= chunk_size)
3867       break;
3868   }
3869
3870   *level = li;          // copy temporary buffer back to level data
3871
3872   return real_chunk_size;
3873 }
3874
3875 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3876 {
3877   int element = getMappedElement(getFile16BitBE(file));
3878   int envelope_nr = element - EL_ENVELOPE_1;
3879   int real_chunk_size = 2;
3880
3881   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3882
3883   while (!checkEndOfFile(file))
3884   {
3885     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3886                                             -1, element);
3887
3888     if (real_chunk_size >= chunk_size)
3889       break;
3890   }
3891
3892   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3893
3894   return real_chunk_size;
3895 }
3896
3897 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3898 {
3899   int element = getMappedElement(getFile16BitBE(file));
3900   int real_chunk_size = 2;
3901   struct ElementInfo *ei = &element_info[element];
3902   int i;
3903
3904   xx_ei = *ei;          // copy element data into temporary buffer
3905
3906   xx_ei.num_change_pages = -1;
3907
3908   while (!checkEndOfFile(file))
3909   {
3910     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3911                                             -1, element);
3912     if (xx_ei.num_change_pages != -1)
3913       break;
3914
3915     if (real_chunk_size >= chunk_size)
3916       break;
3917   }
3918
3919   *ei = xx_ei;
3920
3921   if (ei->num_change_pages == -1)
3922   {
3923     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3924          EL_NAME(element));
3925
3926     ei->num_change_pages = 1;
3927
3928     setElementChangePages(ei, 1);
3929     setElementChangeInfoToDefaults(ei->change);
3930
3931     return real_chunk_size;
3932   }
3933
3934   // initialize number of change pages stored for this custom element
3935   setElementChangePages(ei, ei->num_change_pages);
3936   for (i = 0; i < ei->num_change_pages; i++)
3937     setElementChangeInfoToDefaults(&ei->change_page[i]);
3938
3939   // start with reading properties for the first change page
3940   xx_current_change_page = 0;
3941
3942   while (!checkEndOfFile(file))
3943   {
3944     // level file might contain invalid change page number
3945     if (xx_current_change_page >= ei->num_change_pages)
3946       break;
3947
3948     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3949
3950     xx_change = *change;        // copy change data into temporary buffer
3951
3952     resetEventBits();           // reset bits; change page might have changed
3953
3954     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3955                                             -1, element);
3956
3957     *change = xx_change;
3958
3959     setEventFlagsFromEventBits(change);
3960
3961     if (real_chunk_size >= chunk_size)
3962       break;
3963   }
3964
3965   level->file_has_custom_elements = TRUE;
3966
3967   return real_chunk_size;
3968 }
3969
3970 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3971 {
3972   int element = getMappedElement(getFile16BitBE(file));
3973   int real_chunk_size = 2;
3974   struct ElementInfo *ei = &element_info[element];
3975   struct ElementGroupInfo *group = ei->group;
3976
3977   if (group == NULL)
3978     return -1;
3979
3980   xx_ei = *ei;          // copy element data into temporary buffer
3981   xx_group = *group;    // copy group data into temporary buffer
3982
3983   while (!checkEndOfFile(file))
3984   {
3985     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3986                                             -1, element);
3987
3988     if (real_chunk_size >= chunk_size)
3989       break;
3990   }
3991
3992   *ei = xx_ei;
3993   *group = xx_group;
3994
3995   level->file_has_custom_elements = TRUE;
3996
3997   return real_chunk_size;
3998 }
3999
4000 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
4001 {
4002   int element = getMappedElement(getFile16BitBE(file));
4003   int real_chunk_size = 2;
4004   struct ElementInfo *ei = &element_info[element];
4005
4006   xx_ei = *ei;          // copy element data into temporary buffer
4007
4008   while (!checkEndOfFile(file))
4009   {
4010     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
4011                                             -1, element);
4012
4013     if (real_chunk_size >= chunk_size)
4014       break;
4015   }
4016
4017   *ei = xx_ei;
4018
4019   level->file_has_custom_elements = TRUE;
4020
4021   return real_chunk_size;
4022 }
4023
4024 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
4025                                       struct LevelFileInfo *level_file_info,
4026                                       boolean level_info_only)
4027 {
4028   char *filename = level_file_info->filename;
4029   char cookie[MAX_LINE_LEN];
4030   char chunk_name[CHUNK_ID_LEN + 1];
4031   int chunk_size;
4032   File *file;
4033
4034   if (!(file = openFile(filename, MODE_READ)))
4035   {
4036     level->no_valid_file = TRUE;
4037     level->no_level_file = TRUE;
4038
4039     if (level_info_only)
4040       return;
4041
4042     Warn("cannot read level '%s' -- using empty level", filename);
4043
4044     if (!setup.editor.use_template_for_new_levels)
4045       return;
4046
4047     // if level file not found, try to initialize level data from template
4048     filename = getGlobalLevelTemplateFilename();
4049
4050     if (!(file = openFile(filename, MODE_READ)))
4051       return;
4052
4053     // default: for empty levels, use level template for custom elements
4054     level->use_custom_template = TRUE;
4055
4056     level->no_valid_file = FALSE;
4057   }
4058
4059   getFileChunkBE(file, chunk_name, NULL);
4060   if (strEqual(chunk_name, "RND1"))
4061   {
4062     getFile32BitBE(file);               // not used
4063
4064     getFileChunkBE(file, chunk_name, NULL);
4065     if (!strEqual(chunk_name, "CAVE"))
4066     {
4067       level->no_valid_file = TRUE;
4068
4069       Warn("unknown format of level file '%s'", filename);
4070
4071       closeFile(file);
4072
4073       return;
4074     }
4075   }
4076   else  // check for pre-2.0 file format with cookie string
4077   {
4078     strcpy(cookie, chunk_name);
4079     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
4080       cookie[4] = '\0';
4081     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
4082       cookie[strlen(cookie) - 1] = '\0';
4083
4084     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
4085     {
4086       level->no_valid_file = TRUE;
4087
4088       Warn("unknown format of level file '%s'", filename);
4089
4090       closeFile(file);
4091
4092       return;
4093     }
4094
4095     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
4096     {
4097       level->no_valid_file = TRUE;
4098
4099       Warn("unsupported version of level file '%s'", filename);
4100
4101       closeFile(file);
4102
4103       return;
4104     }
4105
4106     // pre-2.0 level files have no game version, so use file version here
4107     level->game_version = level->file_version;
4108   }
4109
4110   if (level->file_version < FILE_VERSION_1_2)
4111   {
4112     // level files from versions before 1.2.0 without chunk structure
4113     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4114     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4115   }
4116   else
4117   {
4118     static struct
4119     {
4120       char *name;
4121       int size;
4122       int (*loader)(File *, int, struct LevelInfo *);
4123     }
4124     chunk_info[] =
4125     {
4126       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4127       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4128       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4129       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4130       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4131       { "INFO", -1,                     LoadLevel_INFO },
4132       { "BODY", -1,                     LoadLevel_BODY },
4133       { "CONT", -1,                     LoadLevel_CONT },
4134       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4135       { "CNT3", -1,                     LoadLevel_CNT3 },
4136       { "CUS1", -1,                     LoadLevel_CUS1 },
4137       { "CUS2", -1,                     LoadLevel_CUS2 },
4138       { "CUS3", -1,                     LoadLevel_CUS3 },
4139       { "CUS4", -1,                     LoadLevel_CUS4 },
4140       { "GRP1", -1,                     LoadLevel_GRP1 },
4141       { "CONF", -1,                     LoadLevel_CONF },
4142       { "ELEM", -1,                     LoadLevel_ELEM },
4143       { "NOTE", -1,                     LoadLevel_NOTE },
4144       { "CUSX", -1,                     LoadLevel_CUSX },
4145       { "GRPX", -1,                     LoadLevel_GRPX },
4146       { "EMPX", -1,                     LoadLevel_EMPX },
4147
4148       {  NULL,  0,                      NULL }
4149     };
4150
4151     while (getFileChunkBE(file, chunk_name, &chunk_size))
4152     {
4153       int i = 0;
4154
4155       while (chunk_info[i].name != NULL &&
4156              !strEqual(chunk_name, chunk_info[i].name))
4157         i++;
4158
4159       if (chunk_info[i].name == NULL)
4160       {
4161         Warn("unknown chunk '%s' in level file '%s'",
4162              chunk_name, filename);
4163
4164         ReadUnusedBytesFromFile(file, chunk_size);
4165       }
4166       else if (chunk_info[i].size != -1 &&
4167                chunk_info[i].size != chunk_size)
4168       {
4169         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4170              chunk_size, chunk_name, filename);
4171
4172         ReadUnusedBytesFromFile(file, chunk_size);
4173       }
4174       else
4175       {
4176         // call function to load this level chunk
4177         int chunk_size_expected =
4178           (chunk_info[i].loader)(file, chunk_size, level);
4179
4180         if (chunk_size_expected < 0)
4181         {
4182           Warn("error reading chunk '%s' in level file '%s'",
4183                chunk_name, filename);
4184
4185           break;
4186         }
4187
4188         // the size of some chunks cannot be checked before reading other
4189         // chunks first (like "HEAD" and "BODY") that contain some header
4190         // information, so check them here
4191         if (chunk_size_expected != chunk_size)
4192         {
4193           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4194                chunk_size, chunk_name, filename);
4195
4196           break;
4197         }
4198       }
4199     }
4200   }
4201
4202   closeFile(file);
4203 }
4204
4205
4206 // ----------------------------------------------------------------------------
4207 // functions for loading BD level
4208 // ----------------------------------------------------------------------------
4209
4210 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4211 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4212
4213 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4214 {
4215   struct LevelInfo_BD *level_bd = level->native_bd_level;
4216   GdCave *cave = NULL;  // will be changed below
4217   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4218   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4219   int x, y;
4220
4221   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4222
4223   // cave and map newly allocated when set to defaults above
4224   cave = level_bd->cave;
4225
4226   // level type
4227   cave->intermission                    = level->bd_intermission;
4228
4229   // level settings
4230   cave->level_time[0]                   = level->time;
4231   cave->level_diamonds[0]               = level->gems_needed;
4232
4233   // game timing
4234   cave->scheduling                      = level->bd_scheduling_type;
4235   cave->pal_timing                      = level->bd_pal_timing;
4236   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4237   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4238   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4239   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4240
4241   // scores
4242   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4243   cave->diamond_value                   = level->score[SC_EMERALD];
4244   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4245
4246   // compatibility settings
4247   cave->lineshift                       = level->bd_line_shifting_borders;
4248   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4249   cave->short_explosions                = level->bd_short_explosions;
4250
4251   // player properties
4252   cave->diagonal_movements              = level->bd_diagonal_movements;
4253   cave->active_is_first_found           = level->bd_topmost_player_active;
4254   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4255   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4256   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4257   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4258
4259   // element properties
4260   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4261   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4262   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4263   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4264   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4265   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4266   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4267   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4268   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4269
4270   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4271   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4272   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4273   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4274   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4275   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4276   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4277
4278   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4279   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4280   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4281   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4282   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4283   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4284   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4285   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4286   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4287   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4288   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4289
4290   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4291   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4292   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4293   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4294   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4295   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4296
4297   cave->slime_predictable               = level->bd_slime_is_predictable;
4298   cave->slime_correct_random            = level->bd_slime_correct_random;
4299   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4300   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4301   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4302   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4303   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4304   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4305   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4306   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4307   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4308   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4309
4310   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4311   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4312   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4313
4314   cave->biter_delay_frame               = level->bd_biter_move_delay;
4315   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4316
4317   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4318
4319   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4320
4321   cave->replicators_active              = level->bd_replicators_active;
4322   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4323
4324   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4325   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4326
4327   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4328
4329   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4330
4331   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4332   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4333   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4334
4335   cave->infinite_rockets                = level->bd_infinite_rockets;
4336
4337   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4338   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4339
4340   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4341   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4342
4343   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4344   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4345   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4346
4347   cave->gravity                         = level->bd_gravity_direction;
4348   cave->gravity_switch_active           = level->bd_gravity_switch_active;
4349   cave->gravity_change_time             = level->bd_gravity_switch_delay;
4350   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4351
4352   cave->stone_falling_effect            = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_falling);
4353   cave->stone_bouncing_effect           = LEVEL_TO_CAVE(level->bd_rock_turns_to_on_impact);
4354   cave->diamond_falling_effect          = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_falling);
4355   cave->diamond_bouncing_effect         = LEVEL_TO_CAVE(level->bd_diamond_turns_to_on_impact);
4356
4357   cave->firefly_explode_to              = LEVEL_TO_CAVE(level->bd_firefly_explodes_to);
4358   cave->alt_firefly_explode_to          = LEVEL_TO_CAVE(level->bd_firefly_2_explodes_to);
4359   cave->butterfly_explode_to            = LEVEL_TO_CAVE(level->bd_butterfly_explodes_to);
4360   cave->alt_butterfly_explode_to        = LEVEL_TO_CAVE(level->bd_butterfly_2_explodes_to);
4361   cave->stonefly_explode_to             = LEVEL_TO_CAVE(level->bd_stonefly_explodes_to);
4362   cave->dragonfly_explode_to            = LEVEL_TO_CAVE(level->bd_dragonfly_explodes_to);
4363
4364   cave->diamond_birth_effect            = LEVEL_TO_CAVE(level->bd_diamond_birth_turns_to);
4365   cave->bomb_explosion_effect           = LEVEL_TO_CAVE(level->bd_bomb_explosion_turns_to);
4366   cave->nitro_explosion_effect          = LEVEL_TO_CAVE(level->bd_nitro_explosion_turns_to);
4367   cave->explosion_effect                = LEVEL_TO_CAVE(level->bd_explosion_turns_to);
4368
4369   // level name
4370   strncpy(cave->name, level->name, sizeof(GdString));
4371   cave->name[sizeof(GdString) - 1] = '\0';
4372
4373   // playfield elements
4374   for (x = 0; x < cave->w; x++)
4375     for (y = 0; y < cave->h; y++)
4376       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4377 }
4378
4379 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4380 {
4381   struct LevelInfo_BD *level_bd = level->native_bd_level;
4382   GdCave *cave = level_bd->cave;
4383   int bd_level_nr = level_bd->level_nr;
4384   int x, y;
4385
4386   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4387   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4388
4389   // level type
4390   level->bd_intermission                = cave->intermission;
4391
4392   // level settings
4393   level->time                           = cave->level_time[bd_level_nr];
4394   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4395
4396   // game timing
4397   level->bd_scheduling_type             = cave->scheduling;
4398   level->bd_pal_timing                  = cave->pal_timing;
4399   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4400   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4401   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4402   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4403
4404   // scores
4405   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4406   level->score[SC_EMERALD]              = cave->diamond_value;
4407   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4408
4409   // compatibility settings
4410   level->bd_line_shifting_borders       = cave->lineshift;
4411   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4412   level->bd_short_explosions            = cave->short_explosions;
4413
4414   // player properties
4415   level->bd_diagonal_movements          = cave->diagonal_movements;
4416   level->bd_topmost_player_active       = cave->active_is_first_found;
4417   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4418   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4419   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4420   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4421
4422   // element properties
4423   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4424   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4425   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4426   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4427   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4428   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4429   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4430   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4431   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4432
4433   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4434   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4435   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4436   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4437   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4438   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4439   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4440
4441   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4442   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4443   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4444   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4445   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4446   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4447   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4448   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4449   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4450   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4451   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4452
4453   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4454   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4455   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4456   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4457   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4458   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4459
4460   level->bd_slime_is_predictable        = cave->slime_predictable;
4461   level->bd_slime_correct_random        = cave->slime_correct_random;
4462   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4463   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4464   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4465   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4466   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4467   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4468   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4469   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4470   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4471   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4472
4473   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4474   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4475   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4476
4477   level->bd_biter_move_delay            = cave->biter_delay_frame;
4478   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4479
4480   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4481
4482   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4483
4484   level->bd_replicators_active          = cave->replicators_active;
4485   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4486
4487   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4488   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4489
4490   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4491
4492   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4493
4494   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4495   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4496   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4497
4498   level->bd_infinite_rockets            = cave->infinite_rockets;
4499
4500   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4501   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4502
4503   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4504   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4505
4506   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4507   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4508   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4509
4510   level->bd_gravity_direction           = cave->gravity;
4511   level->bd_gravity_switch_active       = cave->gravity_switch_active;
4512   level->bd_gravity_switch_delay        = cave->gravity_change_time;
4513   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4514
4515   level->bd_rock_turns_to_on_falling    = CAVE_TO_LEVEL(cave->stone_falling_effect);
4516   level->bd_rock_turns_to_on_impact     = CAVE_TO_LEVEL(cave->stone_bouncing_effect);
4517   level->bd_diamond_turns_to_on_falling = CAVE_TO_LEVEL(cave->diamond_falling_effect);
4518   level->bd_diamond_turns_to_on_impact  = CAVE_TO_LEVEL(cave->diamond_bouncing_effect);
4519
4520   level->bd_firefly_explodes_to         = CAVE_TO_LEVEL(cave->firefly_explode_to);
4521   level->bd_firefly_2_explodes_to       = CAVE_TO_LEVEL(cave->alt_firefly_explode_to);
4522   level->bd_butterfly_explodes_to       = CAVE_TO_LEVEL(cave->butterfly_explode_to);
4523   level->bd_butterfly_2_explodes_to     = CAVE_TO_LEVEL(cave->alt_butterfly_explode_to);
4524   level->bd_stonefly_explodes_to        = CAVE_TO_LEVEL(cave->stonefly_explode_to);
4525   level->bd_dragonfly_explodes_to       = CAVE_TO_LEVEL(cave->dragonfly_explode_to);
4526
4527   level->bd_diamond_birth_turns_to      = CAVE_TO_LEVEL(cave->diamond_birth_effect);
4528   level->bd_bomb_explosion_turns_to     = CAVE_TO_LEVEL(cave->bomb_explosion_effect);
4529   level->bd_nitro_explosion_turns_to    = CAVE_TO_LEVEL(cave->nitro_explosion_effect);
4530   level->bd_explosion_turns_to          = CAVE_TO_LEVEL(cave->explosion_effect);
4531
4532   // level name
4533   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4534
4535   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4536   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4537
4538   // playfield elements
4539   for (x = 0; x < level->fieldx; x++)
4540     for (y = 0; y < level->fieldy; y++)
4541       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4542
4543   checked_free(cave_name);
4544 }
4545
4546 static void setTapeInfoToDefaults(void);
4547
4548 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4549 {
4550   struct LevelInfo_BD *level_bd = level->native_bd_level;
4551   GdCave *cave = level_bd->cave;
4552   GdReplay *replay = level_bd->replay;
4553   int i;
4554
4555   if (replay == NULL)
4556     return;
4557
4558   // always start with reliable default values
4559   setTapeInfoToDefaults();
4560
4561   tape.level_nr = level_nr;             // (currently not used)
4562   tape.random_seed = replay->seed;
4563
4564   TapeSetDateFromIsoDateString(replay->date);
4565
4566   tape.counter = 0;
4567   tape.pos[tape.counter].delay = 0;
4568
4569   tape.bd_replay = TRUE;
4570
4571   // all time calculations only used to display approximate tape time
4572   int cave_speed = cave->speed;
4573   int milliseconds_game = 0;
4574   int milliseconds_elapsed = 20;
4575
4576   for (i = 0; i < replay->movements->len; i++)
4577   {
4578     int replay_action = replay->movements->data[i];
4579     int tape_action = map_action_BD_to_RND(replay_action);
4580     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4581     boolean success = 0;
4582
4583     while (1)
4584     {
4585       success = TapeAddAction(action);
4586
4587       milliseconds_game += milliseconds_elapsed;
4588
4589       if (milliseconds_game >= cave_speed)
4590       {
4591         milliseconds_game -= cave_speed;
4592
4593         break;
4594       }
4595     }
4596
4597     tape.counter++;
4598     tape.pos[tape.counter].delay = 0;
4599     tape.pos[tape.counter].action[0] = 0;
4600
4601     if (!success)
4602     {
4603       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4604
4605       break;
4606     }
4607   }
4608
4609   TapeHaltRecording();
4610
4611   if (!replay->success)
4612     Warn("BD replay is marked as not successful");
4613 }
4614
4615
4616 // ----------------------------------------------------------------------------
4617 // functions for loading EM level
4618 // ----------------------------------------------------------------------------
4619
4620 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4621 {
4622   static int ball_xy[8][2] =
4623   {
4624     { 0, 0 },
4625     { 1, 0 },
4626     { 2, 0 },
4627     { 0, 1 },
4628     { 2, 1 },
4629     { 0, 2 },
4630     { 1, 2 },
4631     { 2, 2 },
4632   };
4633   struct LevelInfo_EM *level_em = level->native_em_level;
4634   struct CAVE *cav = level_em->cav;
4635   int i, j, x, y;
4636
4637   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4638   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4639
4640   cav->time_seconds     = level->time;
4641   cav->gems_needed      = level->gems_needed;
4642
4643   cav->emerald_score    = level->score[SC_EMERALD];
4644   cav->diamond_score    = level->score[SC_DIAMOND];
4645   cav->alien_score      = level->score[SC_ROBOT];
4646   cav->tank_score       = level->score[SC_SPACESHIP];
4647   cav->bug_score        = level->score[SC_BUG];
4648   cav->eater_score      = level->score[SC_YAMYAM];
4649   cav->nut_score        = level->score[SC_NUT];
4650   cav->dynamite_score   = level->score[SC_DYNAMITE];
4651   cav->key_score        = level->score[SC_KEY];
4652   cav->exit_score       = level->score[SC_TIME_BONUS];
4653
4654   cav->num_eater_arrays = level->num_yamyam_contents;
4655
4656   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4657     for (y = 0; y < 3; y++)
4658       for (x = 0; x < 3; x++)
4659         cav->eater_array[i][y * 3 + x] =
4660           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4661
4662   cav->amoeba_time              = level->amoeba_speed;
4663   cav->wonderwall_time          = level->time_magic_wall;
4664   cav->wheel_time               = level->time_wheel;
4665
4666   cav->android_move_time        = level->android_move_time;
4667   cav->android_clone_time       = level->android_clone_time;
4668   cav->ball_random              = level->ball_random;
4669   cav->ball_active              = level->ball_active_initial;
4670   cav->ball_time                = level->ball_time;
4671   cav->num_ball_arrays          = level->num_ball_contents;
4672
4673   cav->lenses_score             = level->lenses_score;
4674   cav->magnify_score            = level->magnify_score;
4675   cav->slurp_score              = level->slurp_score;
4676
4677   cav->lenses_time              = level->lenses_time;
4678   cav->magnify_time             = level->magnify_time;
4679
4680   cav->wind_time = 9999;
4681   cav->wind_direction =
4682     map_direction_RND_to_EM(level->wind_direction_initial);
4683
4684   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4685     for (j = 0; j < 8; j++)
4686       cav->ball_array[i][j] =
4687         map_element_RND_to_EM_cave(level->ball_content[i].
4688                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4689
4690   map_android_clone_elements_RND_to_EM(level);
4691
4692   // first fill the complete playfield with the empty space element
4693   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4694     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4695       cav->cave[x][y] = Cblank;
4696
4697   // then copy the real level contents from level file into the playfield
4698   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4699   {
4700     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4701
4702     if (level->field[x][y] == EL_AMOEBA_DEAD)
4703       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4704
4705     cav->cave[x][y] = new_element;
4706   }
4707
4708   for (i = 0; i < MAX_PLAYERS; i++)
4709   {
4710     cav->player_x[i] = -1;
4711     cav->player_y[i] = -1;
4712   }
4713
4714   // initialize player positions and delete players from the playfield
4715   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4716   {
4717     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4718     {
4719       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4720
4721       cav->player_x[player_nr] = x;
4722       cav->player_y[player_nr] = y;
4723
4724       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4725     }
4726   }
4727 }
4728
4729 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4730 {
4731   static int ball_xy[8][2] =
4732   {
4733     { 0, 0 },
4734     { 1, 0 },
4735     { 2, 0 },
4736     { 0, 1 },
4737     { 2, 1 },
4738     { 0, 2 },
4739     { 1, 2 },
4740     { 2, 2 },
4741   };
4742   struct LevelInfo_EM *level_em = level->native_em_level;
4743   struct CAVE *cav = level_em->cav;
4744   int i, j, x, y;
4745
4746   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4747   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4748
4749   level->time        = cav->time_seconds;
4750   level->gems_needed = cav->gems_needed;
4751
4752   sprintf(level->name, "Level %d", level->file_info.nr);
4753
4754   level->score[SC_EMERALD]      = cav->emerald_score;
4755   level->score[SC_DIAMOND]      = cav->diamond_score;
4756   level->score[SC_ROBOT]        = cav->alien_score;
4757   level->score[SC_SPACESHIP]    = cav->tank_score;
4758   level->score[SC_BUG]          = cav->bug_score;
4759   level->score[SC_YAMYAM]       = cav->eater_score;
4760   level->score[SC_NUT]          = cav->nut_score;
4761   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4762   level->score[SC_KEY]          = cav->key_score;
4763   level->score[SC_TIME_BONUS]   = cav->exit_score;
4764
4765   level->num_yamyam_contents    = cav->num_eater_arrays;
4766
4767   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4768     for (y = 0; y < 3; y++)
4769       for (x = 0; x < 3; x++)
4770         level->yamyam_content[i].e[x][y] =
4771           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4772
4773   level->amoeba_speed           = cav->amoeba_time;
4774   level->time_magic_wall        = cav->wonderwall_time;
4775   level->time_wheel             = cav->wheel_time;
4776
4777   level->android_move_time      = cav->android_move_time;
4778   level->android_clone_time     = cav->android_clone_time;
4779   level->ball_random            = cav->ball_random;
4780   level->ball_active_initial    = cav->ball_active;
4781   level->ball_time              = cav->ball_time;
4782   level->num_ball_contents      = cav->num_ball_arrays;
4783
4784   level->lenses_score           = cav->lenses_score;
4785   level->magnify_score          = cav->magnify_score;
4786   level->slurp_score            = cav->slurp_score;
4787
4788   level->lenses_time            = cav->lenses_time;
4789   level->magnify_time           = cav->magnify_time;
4790
4791   level->wind_direction_initial =
4792     map_direction_EM_to_RND(cav->wind_direction);
4793
4794   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4795     for (j = 0; j < 8; j++)
4796       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4797         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4798
4799   map_android_clone_elements_EM_to_RND(level);
4800
4801   // convert the playfield (some elements need special treatment)
4802   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4803   {
4804     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4805
4806     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4807       new_element = EL_AMOEBA_DEAD;
4808
4809     level->field[x][y] = new_element;
4810   }
4811
4812   for (i = 0; i < MAX_PLAYERS; i++)
4813   {
4814     // in case of all players set to the same field, use the first player
4815     int nr = MAX_PLAYERS - i - 1;
4816     int jx = cav->player_x[nr];
4817     int jy = cav->player_y[nr];
4818
4819     if (jx != -1 && jy != -1)
4820       level->field[jx][jy] = EL_PLAYER_1 + nr;
4821   }
4822
4823   // time score is counted for each 10 seconds left in Emerald Mine levels
4824   level->time_score_base = 10;
4825 }
4826
4827
4828 // ----------------------------------------------------------------------------
4829 // functions for loading SP level
4830 // ----------------------------------------------------------------------------
4831
4832 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4833 {
4834   struct LevelInfo_SP *level_sp = level->native_sp_level;
4835   LevelInfoType *header = &level_sp->header;
4836   int i, x, y;
4837
4838   level_sp->width  = level->fieldx;
4839   level_sp->height = level->fieldy;
4840
4841   for (x = 0; x < level->fieldx; x++)
4842     for (y = 0; y < level->fieldy; y++)
4843       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4844
4845   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4846
4847   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4848     header->LevelTitle[i] = level->name[i];
4849   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4850
4851   header->InfotronsNeeded = level->gems_needed;
4852
4853   header->SpecialPortCount = 0;
4854
4855   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4856   {
4857     boolean gravity_port_found = FALSE;
4858     boolean gravity_port_valid = FALSE;
4859     int gravity_port_flag;
4860     int gravity_port_base_element;
4861     int element = level->field[x][y];
4862
4863     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4864         element <= EL_SP_GRAVITY_ON_PORT_UP)
4865     {
4866       gravity_port_found = TRUE;
4867       gravity_port_valid = TRUE;
4868       gravity_port_flag = 1;
4869       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4870     }
4871     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4872              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4873     {
4874       gravity_port_found = TRUE;
4875       gravity_port_valid = TRUE;
4876       gravity_port_flag = 0;
4877       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4878     }
4879     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4880              element <= EL_SP_GRAVITY_PORT_UP)
4881     {
4882       // change R'n'D style gravity inverting special port to normal port
4883       // (there are no gravity inverting ports in native Supaplex engine)
4884
4885       gravity_port_found = TRUE;
4886       gravity_port_valid = FALSE;
4887       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4888     }
4889
4890     if (gravity_port_found)
4891     {
4892       if (gravity_port_valid &&
4893           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4894       {
4895         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4896
4897         port->PortLocation = (y * level->fieldx + x) * 2;
4898         port->Gravity = gravity_port_flag;
4899
4900         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4901
4902         header->SpecialPortCount++;
4903       }
4904       else
4905       {
4906         // change special gravity port to normal port
4907
4908         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4909       }
4910
4911       level_sp->playfield[x][y] = element - EL_SP_START;
4912     }
4913   }
4914 }
4915
4916 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4917 {
4918   struct LevelInfo_SP *level_sp = level->native_sp_level;
4919   LevelInfoType *header = &level_sp->header;
4920   boolean num_invalid_elements = 0;
4921   int i, j, x, y;
4922
4923   level->fieldx = level_sp->width;
4924   level->fieldy = level_sp->height;
4925
4926   for (x = 0; x < level->fieldx; x++)
4927   {
4928     for (y = 0; y < level->fieldy; y++)
4929     {
4930       int element_old = level_sp->playfield[x][y];
4931       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4932
4933       if (element_new == EL_UNKNOWN)
4934       {
4935         num_invalid_elements++;
4936
4937         Debug("level:native:SP", "invalid element %d at position %d, %d",
4938               element_old, x, y);
4939       }
4940
4941       level->field[x][y] = element_new;
4942     }
4943   }
4944
4945   if (num_invalid_elements > 0)
4946     Warn("found %d invalid elements%s", num_invalid_elements,
4947          (!options.debug ? " (use '--debug' for more details)" : ""));
4948
4949   for (i = 0; i < MAX_PLAYERS; i++)
4950     level->initial_player_gravity[i] =
4951       (header->InitialGravity == 1 ? TRUE : FALSE);
4952
4953   // skip leading spaces
4954   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4955     if (header->LevelTitle[i] != ' ')
4956       break;
4957
4958   // copy level title
4959   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4960     level->name[j] = header->LevelTitle[i];
4961   level->name[j] = '\0';
4962
4963   // cut trailing spaces
4964   for (; j > 0; j--)
4965     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4966       level->name[j - 1] = '\0';
4967
4968   level->gems_needed = header->InfotronsNeeded;
4969
4970   for (i = 0; i < header->SpecialPortCount; i++)
4971   {
4972     SpecialPortType *port = &header->SpecialPort[i];
4973     int port_location = port->PortLocation;
4974     int gravity = port->Gravity;
4975     int port_x, port_y, port_element;
4976
4977     port_x = (port_location / 2) % level->fieldx;
4978     port_y = (port_location / 2) / level->fieldx;
4979
4980     if (port_x < 0 || port_x >= level->fieldx ||
4981         port_y < 0 || port_y >= level->fieldy)
4982     {
4983       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4984
4985       continue;
4986     }
4987
4988     port_element = level->field[port_x][port_y];
4989
4990     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4991         port_element > EL_SP_GRAVITY_PORT_UP)
4992     {
4993       Warn("no special port at position (%d, %d)", port_x, port_y);
4994
4995       continue;
4996     }
4997
4998     // change previous (wrong) gravity inverting special port to either
4999     // gravity enabling special port or gravity disabling special port
5000     level->field[port_x][port_y] +=
5001       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
5002        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
5003   }
5004
5005   // change special gravity ports without database entries to normal ports
5006   for (x = 0; x < level->fieldx; x++)
5007     for (y = 0; y < level->fieldy; y++)
5008       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
5009           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
5010         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
5011
5012   level->time = 0;                      // no time limit
5013   level->amoeba_speed = 0;
5014   level->time_magic_wall = 0;
5015   level->time_wheel = 0;
5016   level->amoeba_content = EL_EMPTY;
5017
5018   // original Supaplex does not use score values -- rate by playing time
5019   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
5020     level->score[i] = 0;
5021
5022   level->rate_time_over_score = TRUE;
5023
5024   // there are no yamyams in supaplex levels
5025   for (i = 0; i < level->num_yamyam_contents; i++)
5026     for (x = 0; x < 3; x++)
5027       for (y = 0; y < 3; y++)
5028         level->yamyam_content[i].e[x][y] = EL_EMPTY;
5029 }
5030
5031 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
5032 {
5033   struct LevelInfo_SP *level_sp = level->native_sp_level;
5034   struct DemoInfo_SP *demo = &level_sp->demo;
5035   int i, j;
5036
5037   // always start with reliable default values
5038   demo->is_available = FALSE;
5039   demo->length = 0;
5040
5041   if (TAPE_IS_EMPTY(tape))
5042     return;
5043
5044   demo->level_nr = tape.level_nr;       // (currently not used)
5045
5046   level_sp->header.DemoRandomSeed = tape.random_seed;
5047
5048   demo->length = 0;
5049
5050   for (i = 0; i < tape.length; i++)
5051   {
5052     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
5053     int demo_repeat = tape.pos[i].delay;
5054     int demo_entries = (demo_repeat + 15) / 16;
5055
5056     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
5057     {
5058       Warn("tape truncated: size exceeds maximum SP demo size %d",
5059            SP_MAX_TAPE_LEN);
5060
5061       break;
5062     }
5063
5064     for (j = 0; j < demo_repeat / 16; j++)
5065       demo->data[demo->length++] = 0xf0 | demo_action;
5066
5067     if (demo_repeat % 16)
5068       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
5069   }
5070
5071   demo->is_available = TRUE;
5072 }
5073
5074 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
5075 {
5076   struct LevelInfo_SP *level_sp = level->native_sp_level;
5077   struct DemoInfo_SP *demo = &level_sp->demo;
5078   char *filename = level->file_info.filename;
5079   int i;
5080
5081   // always start with reliable default values
5082   setTapeInfoToDefaults();
5083
5084   if (!demo->is_available)
5085     return;
5086
5087   tape.level_nr = demo->level_nr;       // (currently not used)
5088   tape.random_seed = level_sp->header.DemoRandomSeed;
5089
5090   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
5091
5092   tape.counter = 0;
5093   tape.pos[tape.counter].delay = 0;
5094
5095   for (i = 0; i < demo->length; i++)
5096   {
5097     int demo_action = demo->data[i] & 0x0f;
5098     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
5099     int tape_action = map_key_SP_to_RND(demo_action);
5100     int tape_repeat = demo_repeat + 1;
5101     byte action[MAX_TAPE_ACTIONS] = { tape_action };
5102     boolean success = 0;
5103     int j;
5104
5105     for (j = 0; j < tape_repeat; j++)
5106       success = TapeAddAction(action);
5107
5108     if (!success)
5109     {
5110       Warn("SP demo truncated: size exceeds maximum tape size %d",
5111            MAX_TAPE_LEN);
5112
5113       break;
5114     }
5115   }
5116
5117   TapeHaltRecording();
5118 }
5119
5120
5121 // ----------------------------------------------------------------------------
5122 // functions for loading MM level
5123 // ----------------------------------------------------------------------------
5124
5125 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
5126 {
5127   struct LevelInfo_MM *level_mm = level->native_mm_level;
5128   int i, x, y;
5129
5130   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
5131   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
5132
5133   level_mm->time = level->time;
5134   level_mm->kettles_needed = level->gems_needed;
5135   level_mm->auto_count_kettles = level->auto_count_gems;
5136
5137   level_mm->mm_laser_red   = level->mm_laser_red;
5138   level_mm->mm_laser_green = level->mm_laser_green;
5139   level_mm->mm_laser_blue  = level->mm_laser_blue;
5140
5141   level_mm->df_laser_red   = level->df_laser_red;
5142   level_mm->df_laser_green = level->df_laser_green;
5143   level_mm->df_laser_blue  = level->df_laser_blue;
5144
5145   strcpy(level_mm->name, level->name);
5146   strcpy(level_mm->author, level->author);
5147
5148   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
5149   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
5150   level_mm->score[SC_KEY]        = level->score[SC_KEY];
5151   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
5152   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5153
5154   level_mm->amoeba_speed = level->amoeba_speed;
5155   level_mm->time_fuse    = level->mm_time_fuse;
5156   level_mm->time_bomb    = level->mm_time_bomb;
5157   level_mm->time_ball    = level->mm_time_ball;
5158   level_mm->time_block   = level->mm_time_block;
5159
5160   level_mm->num_ball_contents = level->num_mm_ball_contents;
5161   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5162   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5163   level_mm->explode_ball = level->explode_mm_ball;
5164
5165   for (i = 0; i < level->num_mm_ball_contents; i++)
5166     level_mm->ball_content[i] =
5167       map_element_RND_to_MM(level->mm_ball_content[i]);
5168
5169   for (x = 0; x < level->fieldx; x++)
5170     for (y = 0; y < level->fieldy; y++)
5171       Ur[x][y] =
5172         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5173 }
5174
5175 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5176 {
5177   struct LevelInfo_MM *level_mm = level->native_mm_level;
5178   int i, x, y;
5179
5180   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5181   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5182
5183   level->time = level_mm->time;
5184   level->gems_needed = level_mm->kettles_needed;
5185   level->auto_count_gems = level_mm->auto_count_kettles;
5186
5187   level->mm_laser_red   = level_mm->mm_laser_red;
5188   level->mm_laser_green = level_mm->mm_laser_green;
5189   level->mm_laser_blue  = level_mm->mm_laser_blue;
5190
5191   level->df_laser_red   = level_mm->df_laser_red;
5192   level->df_laser_green = level_mm->df_laser_green;
5193   level->df_laser_blue  = level_mm->df_laser_blue;
5194
5195   strcpy(level->name, level_mm->name);
5196
5197   // only overwrite author from 'levelinfo.conf' if author defined in level
5198   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5199     strcpy(level->author, level_mm->author);
5200
5201   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5202   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5203   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5204   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5205   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5206
5207   level->amoeba_speed  = level_mm->amoeba_speed;
5208   level->mm_time_fuse  = level_mm->time_fuse;
5209   level->mm_time_bomb  = level_mm->time_bomb;
5210   level->mm_time_ball  = level_mm->time_ball;
5211   level->mm_time_block = level_mm->time_block;
5212
5213   level->num_mm_ball_contents = level_mm->num_ball_contents;
5214   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5215   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5216   level->explode_mm_ball = level_mm->explode_ball;
5217
5218   for (i = 0; i < level->num_mm_ball_contents; i++)
5219     level->mm_ball_content[i] =
5220       map_element_MM_to_RND(level_mm->ball_content[i]);
5221
5222   for (x = 0; x < level->fieldx; x++)
5223     for (y = 0; y < level->fieldy; y++)
5224       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5225 }
5226
5227
5228 // ----------------------------------------------------------------------------
5229 // functions for loading DC level
5230 // ----------------------------------------------------------------------------
5231
5232 #define DC_LEVEL_HEADER_SIZE            344
5233
5234 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5235                                         boolean init)
5236 {
5237   static int last_data_encoded;
5238   static int offset1;
5239   static int offset2;
5240   int diff;
5241   int diff_hi, diff_lo;
5242   int data_hi, data_lo;
5243   unsigned short data_decoded;
5244
5245   if (init)
5246   {
5247     last_data_encoded = 0;
5248     offset1 = -1;
5249     offset2 = 0;
5250
5251     return 0;
5252   }
5253
5254   diff = data_encoded - last_data_encoded;
5255   diff_hi = diff & ~0xff;
5256   diff_lo = diff &  0xff;
5257
5258   offset2 += diff_lo;
5259
5260   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5261   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5262   data_hi = data_hi & 0xff00;
5263
5264   data_decoded = data_hi | data_lo;
5265
5266   last_data_encoded = data_encoded;
5267
5268   offset1 = (offset1 + 1) % 31;
5269   offset2 = offset2 & 0xff;
5270
5271   return data_decoded;
5272 }
5273
5274 static int getMappedElement_DC(int element)
5275 {
5276   switch (element)
5277   {
5278     case 0x0000:
5279       element = EL_ROCK;
5280       break;
5281
5282       // 0x0117 - 0x036e: (?)
5283       // EL_DIAMOND
5284
5285       // 0x042d - 0x0684: (?)
5286       // EL_EMERALD
5287
5288     case 0x06f1:
5289       element = EL_NUT;
5290       break;
5291
5292     case 0x074c:
5293       element = EL_BOMB;
5294       break;
5295
5296     case 0x07a4:
5297       element = EL_PEARL;
5298       break;
5299
5300     case 0x0823:
5301       element = EL_CRYSTAL;
5302       break;
5303
5304     case 0x0e77:        // quicksand (boulder)
5305       element = EL_QUICKSAND_FAST_FULL;
5306       break;
5307
5308     case 0x0e99:        // slow quicksand (boulder)
5309       element = EL_QUICKSAND_FULL;
5310       break;
5311
5312     case 0x0ed2:
5313       element = EL_EM_EXIT_OPEN;
5314       break;
5315
5316     case 0x0ee3:
5317       element = EL_EM_EXIT_CLOSED;
5318       break;
5319
5320     case 0x0eeb:
5321       element = EL_EM_STEEL_EXIT_OPEN;
5322       break;
5323
5324     case 0x0efc:
5325       element = EL_EM_STEEL_EXIT_CLOSED;
5326       break;
5327
5328     case 0x0f4f:        // dynamite (lit 1)
5329       element = EL_EM_DYNAMITE_ACTIVE;
5330       break;
5331
5332     case 0x0f57:        // dynamite (lit 2)
5333       element = EL_EM_DYNAMITE_ACTIVE;
5334       break;
5335
5336     case 0x0f5f:        // dynamite (lit 3)
5337       element = EL_EM_DYNAMITE_ACTIVE;
5338       break;
5339
5340     case 0x0f67:        // dynamite (lit 4)
5341       element = EL_EM_DYNAMITE_ACTIVE;
5342       break;
5343
5344     case 0x0f81:
5345     case 0x0f82:
5346     case 0x0f83:
5347     case 0x0f84:
5348       element = EL_AMOEBA_WET;
5349       break;
5350
5351     case 0x0f85:
5352       element = EL_AMOEBA_DROP;
5353       break;
5354
5355     case 0x0fb9:
5356       element = EL_DC_MAGIC_WALL;
5357       break;
5358
5359     case 0x0fd0:
5360       element = EL_SPACESHIP_UP;
5361       break;
5362
5363     case 0x0fd9:
5364       element = EL_SPACESHIP_DOWN;
5365       break;
5366
5367     case 0x0ff1:
5368       element = EL_SPACESHIP_LEFT;
5369       break;
5370
5371     case 0x0ff9:
5372       element = EL_SPACESHIP_RIGHT;
5373       break;
5374
5375     case 0x1057:
5376       element = EL_BUG_UP;
5377       break;
5378
5379     case 0x1060:
5380       element = EL_BUG_DOWN;
5381       break;
5382
5383     case 0x1078:
5384       element = EL_BUG_LEFT;
5385       break;
5386
5387     case 0x1080:
5388       element = EL_BUG_RIGHT;
5389       break;
5390
5391     case 0x10de:
5392       element = EL_MOLE_UP;
5393       break;
5394
5395     case 0x10e7:
5396       element = EL_MOLE_DOWN;
5397       break;
5398
5399     case 0x10ff:
5400       element = EL_MOLE_LEFT;
5401       break;
5402
5403     case 0x1107:
5404       element = EL_MOLE_RIGHT;
5405       break;
5406
5407     case 0x11c0:
5408       element = EL_ROBOT;
5409       break;
5410
5411     case 0x13f5:
5412       element = EL_YAMYAM_UP;
5413       break;
5414
5415     case 0x1425:
5416       element = EL_SWITCHGATE_OPEN;
5417       break;
5418
5419     case 0x1426:
5420       element = EL_SWITCHGATE_CLOSED;
5421       break;
5422
5423     case 0x1437:
5424       element = EL_DC_SWITCHGATE_SWITCH_UP;
5425       break;
5426
5427     case 0x143a:
5428       element = EL_TIMEGATE_CLOSED;
5429       break;
5430
5431     case 0x144c:        // conveyor belt switch (green)
5432       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5433       break;
5434
5435     case 0x144f:        // conveyor belt switch (red)
5436       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5437       break;
5438
5439     case 0x1452:        // conveyor belt switch (blue)
5440       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5441       break;
5442
5443     case 0x145b:
5444       element = EL_CONVEYOR_BELT_3_MIDDLE;
5445       break;
5446
5447     case 0x1463:
5448       element = EL_CONVEYOR_BELT_3_LEFT;
5449       break;
5450
5451     case 0x146b:
5452       element = EL_CONVEYOR_BELT_3_RIGHT;
5453       break;
5454
5455     case 0x1473:
5456       element = EL_CONVEYOR_BELT_1_MIDDLE;
5457       break;
5458
5459     case 0x147b:
5460       element = EL_CONVEYOR_BELT_1_LEFT;
5461       break;
5462
5463     case 0x1483:
5464       element = EL_CONVEYOR_BELT_1_RIGHT;
5465       break;
5466
5467     case 0x148b:
5468       element = EL_CONVEYOR_BELT_4_MIDDLE;
5469       break;
5470
5471     case 0x1493:
5472       element = EL_CONVEYOR_BELT_4_LEFT;
5473       break;
5474
5475     case 0x149b:
5476       element = EL_CONVEYOR_BELT_4_RIGHT;
5477       break;
5478
5479     case 0x14ac:
5480       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5481       break;
5482
5483     case 0x14bd:
5484       element = EL_EXPANDABLE_WALL_VERTICAL;
5485       break;
5486
5487     case 0x14c6:
5488       element = EL_EXPANDABLE_WALL_ANY;
5489       break;
5490
5491     case 0x14ce:        // growing steel wall (left/right)
5492       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5493       break;
5494
5495     case 0x14df:        // growing steel wall (up/down)
5496       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5497       break;
5498
5499     case 0x14e8:        // growing steel wall (up/down/left/right)
5500       element = EL_EXPANDABLE_STEELWALL_ANY;
5501       break;
5502
5503     case 0x14e9:
5504       element = EL_SHIELD_DEADLY;
5505       break;
5506
5507     case 0x1501:
5508       element = EL_EXTRA_TIME;
5509       break;
5510
5511     case 0x154f:
5512       element = EL_ACID;
5513       break;
5514
5515     case 0x1577:
5516       element = EL_EMPTY_SPACE;
5517       break;
5518
5519     case 0x1578:        // quicksand (empty)
5520       element = EL_QUICKSAND_FAST_EMPTY;
5521       break;
5522
5523     case 0x1579:        // slow quicksand (empty)
5524       element = EL_QUICKSAND_EMPTY;
5525       break;
5526
5527       // 0x157c - 0x158b:
5528       // EL_SAND
5529
5530       // 0x1590 - 0x159f:
5531       // EL_DC_LANDMINE
5532
5533     case 0x15a0:
5534       element = EL_EM_DYNAMITE;
5535       break;
5536
5537     case 0x15a1:        // key (red)
5538       element = EL_EM_KEY_1;
5539       break;
5540
5541     case 0x15a2:        // key (yellow)
5542       element = EL_EM_KEY_2;
5543       break;
5544
5545     case 0x15a3:        // key (blue)
5546       element = EL_EM_KEY_4;
5547       break;
5548
5549     case 0x15a4:        // key (green)
5550       element = EL_EM_KEY_3;
5551       break;
5552
5553     case 0x15a5:        // key (white)
5554       element = EL_DC_KEY_WHITE;
5555       break;
5556
5557     case 0x15a6:
5558       element = EL_WALL_SLIPPERY;
5559       break;
5560
5561     case 0x15a7:
5562       element = EL_WALL;
5563       break;
5564
5565     case 0x15a8:        // wall (not round)
5566       element = EL_WALL;
5567       break;
5568
5569     case 0x15a9:        // (blue)
5570       element = EL_CHAR_A;
5571       break;
5572
5573     case 0x15aa:        // (blue)
5574       element = EL_CHAR_B;
5575       break;
5576
5577     case 0x15ab:        // (blue)
5578       element = EL_CHAR_C;
5579       break;
5580
5581     case 0x15ac:        // (blue)
5582       element = EL_CHAR_D;
5583       break;
5584
5585     case 0x15ad:        // (blue)
5586       element = EL_CHAR_E;
5587       break;
5588
5589     case 0x15ae:        // (blue)
5590       element = EL_CHAR_F;
5591       break;
5592
5593     case 0x15af:        // (blue)
5594       element = EL_CHAR_G;
5595       break;
5596
5597     case 0x15b0:        // (blue)
5598       element = EL_CHAR_H;
5599       break;
5600
5601     case 0x15b1:        // (blue)
5602       element = EL_CHAR_I;
5603       break;
5604
5605     case 0x15b2:        // (blue)
5606       element = EL_CHAR_J;
5607       break;
5608
5609     case 0x15b3:        // (blue)
5610       element = EL_CHAR_K;
5611       break;
5612
5613     case 0x15b4:        // (blue)
5614       element = EL_CHAR_L;
5615       break;
5616
5617     case 0x15b5:        // (blue)
5618       element = EL_CHAR_M;
5619       break;
5620
5621     case 0x15b6:        // (blue)
5622       element = EL_CHAR_N;
5623       break;
5624
5625     case 0x15b7:        // (blue)
5626       element = EL_CHAR_O;
5627       break;
5628
5629     case 0x15b8:        // (blue)
5630       element = EL_CHAR_P;
5631       break;
5632
5633     case 0x15b9:        // (blue)
5634       element = EL_CHAR_Q;
5635       break;
5636
5637     case 0x15ba:        // (blue)
5638       element = EL_CHAR_R;
5639       break;
5640
5641     case 0x15bb:        // (blue)
5642       element = EL_CHAR_S;
5643       break;
5644
5645     case 0x15bc:        // (blue)
5646       element = EL_CHAR_T;
5647       break;
5648
5649     case 0x15bd:        // (blue)
5650       element = EL_CHAR_U;
5651       break;
5652
5653     case 0x15be:        // (blue)
5654       element = EL_CHAR_V;
5655       break;
5656
5657     case 0x15bf:        // (blue)
5658       element = EL_CHAR_W;
5659       break;
5660
5661     case 0x15c0:        // (blue)
5662       element = EL_CHAR_X;
5663       break;
5664
5665     case 0x15c1:        // (blue)
5666       element = EL_CHAR_Y;
5667       break;
5668
5669     case 0x15c2:        // (blue)
5670       element = EL_CHAR_Z;
5671       break;
5672
5673     case 0x15c3:        // (blue)
5674       element = EL_CHAR_AUMLAUT;
5675       break;
5676
5677     case 0x15c4:        // (blue)
5678       element = EL_CHAR_OUMLAUT;
5679       break;
5680
5681     case 0x15c5:        // (blue)
5682       element = EL_CHAR_UUMLAUT;
5683       break;
5684
5685     case 0x15c6:        // (blue)
5686       element = EL_CHAR_0;
5687       break;
5688
5689     case 0x15c7:        // (blue)
5690       element = EL_CHAR_1;
5691       break;
5692
5693     case 0x15c8:        // (blue)
5694       element = EL_CHAR_2;
5695       break;
5696
5697     case 0x15c9:        // (blue)
5698       element = EL_CHAR_3;
5699       break;
5700
5701     case 0x15ca:        // (blue)
5702       element = EL_CHAR_4;
5703       break;
5704
5705     case 0x15cb:        // (blue)
5706       element = EL_CHAR_5;
5707       break;
5708
5709     case 0x15cc:        // (blue)
5710       element = EL_CHAR_6;
5711       break;
5712
5713     case 0x15cd:        // (blue)
5714       element = EL_CHAR_7;
5715       break;
5716
5717     case 0x15ce:        // (blue)
5718       element = EL_CHAR_8;
5719       break;
5720
5721     case 0x15cf:        // (blue)
5722       element = EL_CHAR_9;
5723       break;
5724
5725     case 0x15d0:        // (blue)
5726       element = EL_CHAR_PERIOD;
5727       break;
5728
5729     case 0x15d1:        // (blue)
5730       element = EL_CHAR_EXCLAM;
5731       break;
5732
5733     case 0x15d2:        // (blue)
5734       element = EL_CHAR_COLON;
5735       break;
5736
5737     case 0x15d3:        // (blue)
5738       element = EL_CHAR_LESS;
5739       break;
5740
5741     case 0x15d4:        // (blue)
5742       element = EL_CHAR_GREATER;
5743       break;
5744
5745     case 0x15d5:        // (blue)
5746       element = EL_CHAR_QUESTION;
5747       break;
5748
5749     case 0x15d6:        // (blue)
5750       element = EL_CHAR_COPYRIGHT;
5751       break;
5752
5753     case 0x15d7:        // (blue)
5754       element = EL_CHAR_UP;
5755       break;
5756
5757     case 0x15d8:        // (blue)
5758       element = EL_CHAR_DOWN;
5759       break;
5760
5761     case 0x15d9:        // (blue)
5762       element = EL_CHAR_BUTTON;
5763       break;
5764
5765     case 0x15da:        // (blue)
5766       element = EL_CHAR_PLUS;
5767       break;
5768
5769     case 0x15db:        // (blue)
5770       element = EL_CHAR_MINUS;
5771       break;
5772
5773     case 0x15dc:        // (blue)
5774       element = EL_CHAR_APOSTROPHE;
5775       break;
5776
5777     case 0x15dd:        // (blue)
5778       element = EL_CHAR_PARENLEFT;
5779       break;
5780
5781     case 0x15de:        // (blue)
5782       element = EL_CHAR_PARENRIGHT;
5783       break;
5784
5785     case 0x15df:        // (green)
5786       element = EL_CHAR_A;
5787       break;
5788
5789     case 0x15e0:        // (green)
5790       element = EL_CHAR_B;
5791       break;
5792
5793     case 0x15e1:        // (green)
5794       element = EL_CHAR_C;
5795       break;
5796
5797     case 0x15e2:        // (green)
5798       element = EL_CHAR_D;
5799       break;
5800
5801     case 0x15e3:        // (green)
5802       element = EL_CHAR_E;
5803       break;
5804
5805     case 0x15e4:        // (green)
5806       element = EL_CHAR_F;
5807       break;
5808
5809     case 0x15e5:        // (green)
5810       element = EL_CHAR_G;
5811       break;
5812
5813     case 0x15e6:        // (green)
5814       element = EL_CHAR_H;
5815       break;
5816
5817     case 0x15e7:        // (green)
5818       element = EL_CHAR_I;
5819       break;
5820
5821     case 0x15e8:        // (green)
5822       element = EL_CHAR_J;
5823       break;
5824
5825     case 0x15e9:        // (green)
5826       element = EL_CHAR_K;
5827       break;
5828
5829     case 0x15ea:        // (green)
5830       element = EL_CHAR_L;
5831       break;
5832
5833     case 0x15eb:        // (green)
5834       element = EL_CHAR_M;
5835       break;
5836
5837     case 0x15ec:        // (green)
5838       element = EL_CHAR_N;
5839       break;
5840
5841     case 0x15ed:        // (green)
5842       element = EL_CHAR_O;
5843       break;
5844
5845     case 0x15ee:        // (green)
5846       element = EL_CHAR_P;
5847       break;
5848
5849     case 0x15ef:        // (green)
5850       element = EL_CHAR_Q;
5851       break;
5852
5853     case 0x15f0:        // (green)
5854       element = EL_CHAR_R;
5855       break;
5856
5857     case 0x15f1:        // (green)
5858       element = EL_CHAR_S;
5859       break;
5860
5861     case 0x15f2:        // (green)
5862       element = EL_CHAR_T;
5863       break;
5864
5865     case 0x15f3:        // (green)
5866       element = EL_CHAR_U;
5867       break;
5868
5869     case 0x15f4:        // (green)
5870       element = EL_CHAR_V;
5871       break;
5872
5873     case 0x15f5:        // (green)
5874       element = EL_CHAR_W;
5875       break;
5876
5877     case 0x15f6:        // (green)
5878       element = EL_CHAR_X;
5879       break;
5880
5881     case 0x15f7:        // (green)
5882       element = EL_CHAR_Y;
5883       break;
5884
5885     case 0x15f8:        // (green)
5886       element = EL_CHAR_Z;
5887       break;
5888
5889     case 0x15f9:        // (green)
5890       element = EL_CHAR_AUMLAUT;
5891       break;
5892
5893     case 0x15fa:        // (green)
5894       element = EL_CHAR_OUMLAUT;
5895       break;
5896
5897     case 0x15fb:        // (green)
5898       element = EL_CHAR_UUMLAUT;
5899       break;
5900
5901     case 0x15fc:        // (green)
5902       element = EL_CHAR_0;
5903       break;
5904
5905     case 0x15fd:        // (green)
5906       element = EL_CHAR_1;
5907       break;
5908
5909     case 0x15fe:        // (green)
5910       element = EL_CHAR_2;
5911       break;
5912
5913     case 0x15ff:        // (green)
5914       element = EL_CHAR_3;
5915       break;
5916
5917     case 0x1600:        // (green)
5918       element = EL_CHAR_4;
5919       break;
5920
5921     case 0x1601:        // (green)
5922       element = EL_CHAR_5;
5923       break;
5924
5925     case 0x1602:        // (green)
5926       element = EL_CHAR_6;
5927       break;
5928
5929     case 0x1603:        // (green)
5930       element = EL_CHAR_7;
5931       break;
5932
5933     case 0x1604:        // (green)
5934       element = EL_CHAR_8;
5935       break;
5936
5937     case 0x1605:        // (green)
5938       element = EL_CHAR_9;
5939       break;
5940
5941     case 0x1606:        // (green)
5942       element = EL_CHAR_PERIOD;
5943       break;
5944
5945     case 0x1607:        // (green)
5946       element = EL_CHAR_EXCLAM;
5947       break;
5948
5949     case 0x1608:        // (green)
5950       element = EL_CHAR_COLON;
5951       break;
5952
5953     case 0x1609:        // (green)
5954       element = EL_CHAR_LESS;
5955       break;
5956
5957     case 0x160a:        // (green)
5958       element = EL_CHAR_GREATER;
5959       break;
5960
5961     case 0x160b:        // (green)
5962       element = EL_CHAR_QUESTION;
5963       break;
5964
5965     case 0x160c:        // (green)
5966       element = EL_CHAR_COPYRIGHT;
5967       break;
5968
5969     case 0x160d:        // (green)
5970       element = EL_CHAR_UP;
5971       break;
5972
5973     case 0x160e:        // (green)
5974       element = EL_CHAR_DOWN;
5975       break;
5976
5977     case 0x160f:        // (green)
5978       element = EL_CHAR_BUTTON;
5979       break;
5980
5981     case 0x1610:        // (green)
5982       element = EL_CHAR_PLUS;
5983       break;
5984
5985     case 0x1611:        // (green)
5986       element = EL_CHAR_MINUS;
5987       break;
5988
5989     case 0x1612:        // (green)
5990       element = EL_CHAR_APOSTROPHE;
5991       break;
5992
5993     case 0x1613:        // (green)
5994       element = EL_CHAR_PARENLEFT;
5995       break;
5996
5997     case 0x1614:        // (green)
5998       element = EL_CHAR_PARENRIGHT;
5999       break;
6000
6001     case 0x1615:        // (blue steel)
6002       element = EL_STEEL_CHAR_A;
6003       break;
6004
6005     case 0x1616:        // (blue steel)
6006       element = EL_STEEL_CHAR_B;
6007       break;
6008
6009     case 0x1617:        // (blue steel)
6010       element = EL_STEEL_CHAR_C;
6011       break;
6012
6013     case 0x1618:        // (blue steel)
6014       element = EL_STEEL_CHAR_D;
6015       break;
6016
6017     case 0x1619:        // (blue steel)
6018       element = EL_STEEL_CHAR_E;
6019       break;
6020
6021     case 0x161a:        // (blue steel)
6022       element = EL_STEEL_CHAR_F;
6023       break;
6024
6025     case 0x161b:        // (blue steel)
6026       element = EL_STEEL_CHAR_G;
6027       break;
6028
6029     case 0x161c:        // (blue steel)
6030       element = EL_STEEL_CHAR_H;
6031       break;
6032
6033     case 0x161d:        // (blue steel)
6034       element = EL_STEEL_CHAR_I;
6035       break;
6036
6037     case 0x161e:        // (blue steel)
6038       element = EL_STEEL_CHAR_J;
6039       break;
6040
6041     case 0x161f:        // (blue steel)
6042       element = EL_STEEL_CHAR_K;
6043       break;
6044
6045     case 0x1620:        // (blue steel)
6046       element = EL_STEEL_CHAR_L;
6047       break;
6048
6049     case 0x1621:        // (blue steel)
6050       element = EL_STEEL_CHAR_M;
6051       break;
6052
6053     case 0x1622:        // (blue steel)
6054       element = EL_STEEL_CHAR_N;
6055       break;
6056
6057     case 0x1623:        // (blue steel)
6058       element = EL_STEEL_CHAR_O;
6059       break;
6060
6061     case 0x1624:        // (blue steel)
6062       element = EL_STEEL_CHAR_P;
6063       break;
6064
6065     case 0x1625:        // (blue steel)
6066       element = EL_STEEL_CHAR_Q;
6067       break;
6068
6069     case 0x1626:        // (blue steel)
6070       element = EL_STEEL_CHAR_R;
6071       break;
6072
6073     case 0x1627:        // (blue steel)
6074       element = EL_STEEL_CHAR_S;
6075       break;
6076
6077     case 0x1628:        // (blue steel)
6078       element = EL_STEEL_CHAR_T;
6079       break;
6080
6081     case 0x1629:        // (blue steel)
6082       element = EL_STEEL_CHAR_U;
6083       break;
6084
6085     case 0x162a:        // (blue steel)
6086       element = EL_STEEL_CHAR_V;
6087       break;
6088
6089     case 0x162b:        // (blue steel)
6090       element = EL_STEEL_CHAR_W;
6091       break;
6092
6093     case 0x162c:        // (blue steel)
6094       element = EL_STEEL_CHAR_X;
6095       break;
6096
6097     case 0x162d:        // (blue steel)
6098       element = EL_STEEL_CHAR_Y;
6099       break;
6100
6101     case 0x162e:        // (blue steel)
6102       element = EL_STEEL_CHAR_Z;
6103       break;
6104
6105     case 0x162f:        // (blue steel)
6106       element = EL_STEEL_CHAR_AUMLAUT;
6107       break;
6108
6109     case 0x1630:        // (blue steel)
6110       element = EL_STEEL_CHAR_OUMLAUT;
6111       break;
6112
6113     case 0x1631:        // (blue steel)
6114       element = EL_STEEL_CHAR_UUMLAUT;
6115       break;
6116
6117     case 0x1632:        // (blue steel)
6118       element = EL_STEEL_CHAR_0;
6119       break;
6120
6121     case 0x1633:        // (blue steel)
6122       element = EL_STEEL_CHAR_1;
6123       break;
6124
6125     case 0x1634:        // (blue steel)
6126       element = EL_STEEL_CHAR_2;
6127       break;
6128
6129     case 0x1635:        // (blue steel)
6130       element = EL_STEEL_CHAR_3;
6131       break;
6132
6133     case 0x1636:        // (blue steel)
6134       element = EL_STEEL_CHAR_4;
6135       break;
6136
6137     case 0x1637:        // (blue steel)
6138       element = EL_STEEL_CHAR_5;
6139       break;
6140
6141     case 0x1638:        // (blue steel)
6142       element = EL_STEEL_CHAR_6;
6143       break;
6144
6145     case 0x1639:        // (blue steel)
6146       element = EL_STEEL_CHAR_7;
6147       break;
6148
6149     case 0x163a:        // (blue steel)
6150       element = EL_STEEL_CHAR_8;
6151       break;
6152
6153     case 0x163b:        // (blue steel)
6154       element = EL_STEEL_CHAR_9;
6155       break;
6156
6157     case 0x163c:        // (blue steel)
6158       element = EL_STEEL_CHAR_PERIOD;
6159       break;
6160
6161     case 0x163d:        // (blue steel)
6162       element = EL_STEEL_CHAR_EXCLAM;
6163       break;
6164
6165     case 0x163e:        // (blue steel)
6166       element = EL_STEEL_CHAR_COLON;
6167       break;
6168
6169     case 0x163f:        // (blue steel)
6170       element = EL_STEEL_CHAR_LESS;
6171       break;
6172
6173     case 0x1640:        // (blue steel)
6174       element = EL_STEEL_CHAR_GREATER;
6175       break;
6176
6177     case 0x1641:        // (blue steel)
6178       element = EL_STEEL_CHAR_QUESTION;
6179       break;
6180
6181     case 0x1642:        // (blue steel)
6182       element = EL_STEEL_CHAR_COPYRIGHT;
6183       break;
6184
6185     case 0x1643:        // (blue steel)
6186       element = EL_STEEL_CHAR_UP;
6187       break;
6188
6189     case 0x1644:        // (blue steel)
6190       element = EL_STEEL_CHAR_DOWN;
6191       break;
6192
6193     case 0x1645:        // (blue steel)
6194       element = EL_STEEL_CHAR_BUTTON;
6195       break;
6196
6197     case 0x1646:        // (blue steel)
6198       element = EL_STEEL_CHAR_PLUS;
6199       break;
6200
6201     case 0x1647:        // (blue steel)
6202       element = EL_STEEL_CHAR_MINUS;
6203       break;
6204
6205     case 0x1648:        // (blue steel)
6206       element = EL_STEEL_CHAR_APOSTROPHE;
6207       break;
6208
6209     case 0x1649:        // (blue steel)
6210       element = EL_STEEL_CHAR_PARENLEFT;
6211       break;
6212
6213     case 0x164a:        // (blue steel)
6214       element = EL_STEEL_CHAR_PARENRIGHT;
6215       break;
6216
6217     case 0x164b:        // (green steel)
6218       element = EL_STEEL_CHAR_A;
6219       break;
6220
6221     case 0x164c:        // (green steel)
6222       element = EL_STEEL_CHAR_B;
6223       break;
6224
6225     case 0x164d:        // (green steel)
6226       element = EL_STEEL_CHAR_C;
6227       break;
6228
6229     case 0x164e:        // (green steel)
6230       element = EL_STEEL_CHAR_D;
6231       break;
6232
6233     case 0x164f:        // (green steel)
6234       element = EL_STEEL_CHAR_E;
6235       break;
6236
6237     case 0x1650:        // (green steel)
6238       element = EL_STEEL_CHAR_F;
6239       break;
6240
6241     case 0x1651:        // (green steel)
6242       element = EL_STEEL_CHAR_G;
6243       break;
6244
6245     case 0x1652:        // (green steel)
6246       element = EL_STEEL_CHAR_H;
6247       break;
6248
6249     case 0x1653:        // (green steel)
6250       element = EL_STEEL_CHAR_I;
6251       break;
6252
6253     case 0x1654:        // (green steel)
6254       element = EL_STEEL_CHAR_J;
6255       break;
6256
6257     case 0x1655:        // (green steel)
6258       element = EL_STEEL_CHAR_K;
6259       break;
6260
6261     case 0x1656:        // (green steel)
6262       element = EL_STEEL_CHAR_L;
6263       break;
6264
6265     case 0x1657:        // (green steel)
6266       element = EL_STEEL_CHAR_M;
6267       break;
6268
6269     case 0x1658:        // (green steel)
6270       element = EL_STEEL_CHAR_N;
6271       break;
6272
6273     case 0x1659:        // (green steel)
6274       element = EL_STEEL_CHAR_O;
6275       break;
6276
6277     case 0x165a:        // (green steel)
6278       element = EL_STEEL_CHAR_P;
6279       break;
6280
6281     case 0x165b:        // (green steel)
6282       element = EL_STEEL_CHAR_Q;
6283       break;
6284
6285     case 0x165c:        // (green steel)
6286       element = EL_STEEL_CHAR_R;
6287       break;
6288
6289     case 0x165d:        // (green steel)
6290       element = EL_STEEL_CHAR_S;
6291       break;
6292
6293     case 0x165e:        // (green steel)
6294       element = EL_STEEL_CHAR_T;
6295       break;
6296
6297     case 0x165f:        // (green steel)
6298       element = EL_STEEL_CHAR_U;
6299       break;
6300
6301     case 0x1660:        // (green steel)
6302       element = EL_STEEL_CHAR_V;
6303       break;
6304
6305     case 0x1661:        // (green steel)
6306       element = EL_STEEL_CHAR_W;
6307       break;
6308
6309     case 0x1662:        // (green steel)
6310       element = EL_STEEL_CHAR_X;
6311       break;
6312
6313     case 0x1663:        // (green steel)
6314       element = EL_STEEL_CHAR_Y;
6315       break;
6316
6317     case 0x1664:        // (green steel)
6318       element = EL_STEEL_CHAR_Z;
6319       break;
6320
6321     case 0x1665:        // (green steel)
6322       element = EL_STEEL_CHAR_AUMLAUT;
6323       break;
6324
6325     case 0x1666:        // (green steel)
6326       element = EL_STEEL_CHAR_OUMLAUT;
6327       break;
6328
6329     case 0x1667:        // (green steel)
6330       element = EL_STEEL_CHAR_UUMLAUT;
6331       break;
6332
6333     case 0x1668:        // (green steel)
6334       element = EL_STEEL_CHAR_0;
6335       break;
6336
6337     case 0x1669:        // (green steel)
6338       element = EL_STEEL_CHAR_1;
6339       break;
6340
6341     case 0x166a:        // (green steel)
6342       element = EL_STEEL_CHAR_2;
6343       break;
6344
6345     case 0x166b:        // (green steel)
6346       element = EL_STEEL_CHAR_3;
6347       break;
6348
6349     case 0x166c:        // (green steel)
6350       element = EL_STEEL_CHAR_4;
6351       break;
6352
6353     case 0x166d:        // (green steel)
6354       element = EL_STEEL_CHAR_5;
6355       break;
6356
6357     case 0x166e:        // (green steel)
6358       element = EL_STEEL_CHAR_6;
6359       break;
6360
6361     case 0x166f:        // (green steel)
6362       element = EL_STEEL_CHAR_7;
6363       break;
6364
6365     case 0x1670:        // (green steel)
6366       element = EL_STEEL_CHAR_8;
6367       break;
6368
6369     case 0x1671:        // (green steel)
6370       element = EL_STEEL_CHAR_9;
6371       break;
6372
6373     case 0x1672:        // (green steel)
6374       element = EL_STEEL_CHAR_PERIOD;
6375       break;
6376
6377     case 0x1673:        // (green steel)
6378       element = EL_STEEL_CHAR_EXCLAM;
6379       break;
6380
6381     case 0x1674:        // (green steel)
6382       element = EL_STEEL_CHAR_COLON;
6383       break;
6384
6385     case 0x1675:        // (green steel)
6386       element = EL_STEEL_CHAR_LESS;
6387       break;
6388
6389     case 0x1676:        // (green steel)
6390       element = EL_STEEL_CHAR_GREATER;
6391       break;
6392
6393     case 0x1677:        // (green steel)
6394       element = EL_STEEL_CHAR_QUESTION;
6395       break;
6396
6397     case 0x1678:        // (green steel)
6398       element = EL_STEEL_CHAR_COPYRIGHT;
6399       break;
6400
6401     case 0x1679:        // (green steel)
6402       element = EL_STEEL_CHAR_UP;
6403       break;
6404
6405     case 0x167a:        // (green steel)
6406       element = EL_STEEL_CHAR_DOWN;
6407       break;
6408
6409     case 0x167b:        // (green steel)
6410       element = EL_STEEL_CHAR_BUTTON;
6411       break;
6412
6413     case 0x167c:        // (green steel)
6414       element = EL_STEEL_CHAR_PLUS;
6415       break;
6416
6417     case 0x167d:        // (green steel)
6418       element = EL_STEEL_CHAR_MINUS;
6419       break;
6420
6421     case 0x167e:        // (green steel)
6422       element = EL_STEEL_CHAR_APOSTROPHE;
6423       break;
6424
6425     case 0x167f:        // (green steel)
6426       element = EL_STEEL_CHAR_PARENLEFT;
6427       break;
6428
6429     case 0x1680:        // (green steel)
6430       element = EL_STEEL_CHAR_PARENRIGHT;
6431       break;
6432
6433     case 0x1681:        // gate (red)
6434       element = EL_EM_GATE_1;
6435       break;
6436
6437     case 0x1682:        // secret gate (red)
6438       element = EL_EM_GATE_1_GRAY;
6439       break;
6440
6441     case 0x1683:        // gate (yellow)
6442       element = EL_EM_GATE_2;
6443       break;
6444
6445     case 0x1684:        // secret gate (yellow)
6446       element = EL_EM_GATE_2_GRAY;
6447       break;
6448
6449     case 0x1685:        // gate (blue)
6450       element = EL_EM_GATE_4;
6451       break;
6452
6453     case 0x1686:        // secret gate (blue)
6454       element = EL_EM_GATE_4_GRAY;
6455       break;
6456
6457     case 0x1687:        // gate (green)
6458       element = EL_EM_GATE_3;
6459       break;
6460
6461     case 0x1688:        // secret gate (green)
6462       element = EL_EM_GATE_3_GRAY;
6463       break;
6464
6465     case 0x1689:        // gate (white)
6466       element = EL_DC_GATE_WHITE;
6467       break;
6468
6469     case 0x168a:        // secret gate (white)
6470       element = EL_DC_GATE_WHITE_GRAY;
6471       break;
6472
6473     case 0x168b:        // secret gate (no key)
6474       element = EL_DC_GATE_FAKE_GRAY;
6475       break;
6476
6477     case 0x168c:
6478       element = EL_ROBOT_WHEEL;
6479       break;
6480
6481     case 0x168d:
6482       element = EL_DC_TIMEGATE_SWITCH;
6483       break;
6484
6485     case 0x168e:
6486       element = EL_ACID_POOL_BOTTOM;
6487       break;
6488
6489     case 0x168f:
6490       element = EL_ACID_POOL_TOPLEFT;
6491       break;
6492
6493     case 0x1690:
6494       element = EL_ACID_POOL_TOPRIGHT;
6495       break;
6496
6497     case 0x1691:
6498       element = EL_ACID_POOL_BOTTOMLEFT;
6499       break;
6500
6501     case 0x1692:
6502       element = EL_ACID_POOL_BOTTOMRIGHT;
6503       break;
6504
6505     case 0x1693:
6506       element = EL_STEELWALL;
6507       break;
6508
6509     case 0x1694:
6510       element = EL_STEELWALL_SLIPPERY;
6511       break;
6512
6513     case 0x1695:        // steel wall (not round)
6514       element = EL_STEELWALL;
6515       break;
6516
6517     case 0x1696:        // steel wall (left)
6518       element = EL_DC_STEELWALL_1_LEFT;
6519       break;
6520
6521     case 0x1697:        // steel wall (bottom)
6522       element = EL_DC_STEELWALL_1_BOTTOM;
6523       break;
6524
6525     case 0x1698:        // steel wall (right)
6526       element = EL_DC_STEELWALL_1_RIGHT;
6527       break;
6528
6529     case 0x1699:        // steel wall (top)
6530       element = EL_DC_STEELWALL_1_TOP;
6531       break;
6532
6533     case 0x169a:        // steel wall (left/bottom)
6534       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6535       break;
6536
6537     case 0x169b:        // steel wall (right/bottom)
6538       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6539       break;
6540
6541     case 0x169c:        // steel wall (right/top)
6542       element = EL_DC_STEELWALL_1_TOPRIGHT;
6543       break;
6544
6545     case 0x169d:        // steel wall (left/top)
6546       element = EL_DC_STEELWALL_1_TOPLEFT;
6547       break;
6548
6549     case 0x169e:        // steel wall (right/bottom small)
6550       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6551       break;
6552
6553     case 0x169f:        // steel wall (left/bottom small)
6554       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6555       break;
6556
6557     case 0x16a0:        // steel wall (right/top small)
6558       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6559       break;
6560
6561     case 0x16a1:        // steel wall (left/top small)
6562       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6563       break;
6564
6565     case 0x16a2:        // steel wall (left/right)
6566       element = EL_DC_STEELWALL_1_VERTICAL;
6567       break;
6568
6569     case 0x16a3:        // steel wall (top/bottom)
6570       element = EL_DC_STEELWALL_1_HORIZONTAL;
6571       break;
6572
6573     case 0x16a4:        // steel wall 2 (left end)
6574       element = EL_DC_STEELWALL_2_LEFT;
6575       break;
6576
6577     case 0x16a5:        // steel wall 2 (right end)
6578       element = EL_DC_STEELWALL_2_RIGHT;
6579       break;
6580
6581     case 0x16a6:        // steel wall 2 (top end)
6582       element = EL_DC_STEELWALL_2_TOP;
6583       break;
6584
6585     case 0x16a7:        // steel wall 2 (bottom end)
6586       element = EL_DC_STEELWALL_2_BOTTOM;
6587       break;
6588
6589     case 0x16a8:        // steel wall 2 (left/right)
6590       element = EL_DC_STEELWALL_2_HORIZONTAL;
6591       break;
6592
6593     case 0x16a9:        // steel wall 2 (up/down)
6594       element = EL_DC_STEELWALL_2_VERTICAL;
6595       break;
6596
6597     case 0x16aa:        // steel wall 2 (mid)
6598       element = EL_DC_STEELWALL_2_MIDDLE;
6599       break;
6600
6601     case 0x16ab:
6602       element = EL_SIGN_EXCLAMATION;
6603       break;
6604
6605     case 0x16ac:
6606       element = EL_SIGN_RADIOACTIVITY;
6607       break;
6608
6609     case 0x16ad:
6610       element = EL_SIGN_STOP;
6611       break;
6612
6613     case 0x16ae:
6614       element = EL_SIGN_WHEELCHAIR;
6615       break;
6616
6617     case 0x16af:
6618       element = EL_SIGN_PARKING;
6619       break;
6620
6621     case 0x16b0:
6622       element = EL_SIGN_NO_ENTRY;
6623       break;
6624
6625     case 0x16b1:
6626       element = EL_SIGN_HEART;
6627       break;
6628
6629     case 0x16b2:
6630       element = EL_SIGN_GIVE_WAY;
6631       break;
6632
6633     case 0x16b3:
6634       element = EL_SIGN_ENTRY_FORBIDDEN;
6635       break;
6636
6637     case 0x16b4:
6638       element = EL_SIGN_EMERGENCY_EXIT;
6639       break;
6640
6641     case 0x16b5:
6642       element = EL_SIGN_YIN_YANG;
6643       break;
6644
6645     case 0x16b6:
6646       element = EL_WALL_EMERALD;
6647       break;
6648
6649     case 0x16b7:
6650       element = EL_WALL_DIAMOND;
6651       break;
6652
6653     case 0x16b8:
6654       element = EL_WALL_PEARL;
6655       break;
6656
6657     case 0x16b9:
6658       element = EL_WALL_CRYSTAL;
6659       break;
6660
6661     case 0x16ba:
6662       element = EL_INVISIBLE_WALL;
6663       break;
6664
6665     case 0x16bb:
6666       element = EL_INVISIBLE_STEELWALL;
6667       break;
6668
6669       // 0x16bc - 0x16cb:
6670       // EL_INVISIBLE_SAND
6671
6672     case 0x16cc:
6673       element = EL_LIGHT_SWITCH;
6674       break;
6675
6676     case 0x16cd:
6677       element = EL_ENVELOPE_1;
6678       break;
6679
6680     default:
6681       if (element >= 0x0117 && element <= 0x036e)       // (?)
6682         element = EL_DIAMOND;
6683       else if (element >= 0x042d && element <= 0x0684)  // (?)
6684         element = EL_EMERALD;
6685       else if (element >= 0x157c && element <= 0x158b)
6686         element = EL_SAND;
6687       else if (element >= 0x1590 && element <= 0x159f)
6688         element = EL_DC_LANDMINE;
6689       else if (element >= 0x16bc && element <= 0x16cb)
6690         element = EL_INVISIBLE_SAND;
6691       else
6692       {
6693         Warn("unknown Diamond Caves element 0x%04x", element);
6694
6695         element = EL_UNKNOWN;
6696       }
6697       break;
6698   }
6699
6700   return getMappedElement(element);
6701 }
6702
6703 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6704 {
6705   byte header[DC_LEVEL_HEADER_SIZE];
6706   int envelope_size;
6707   int envelope_header_pos = 62;
6708   int envelope_content_pos = 94;
6709   int level_name_pos = 251;
6710   int level_author_pos = 292;
6711   int envelope_header_len;
6712   int envelope_content_len;
6713   int level_name_len;
6714   int level_author_len;
6715   int fieldx, fieldy;
6716   int num_yamyam_contents;
6717   int i, x, y;
6718
6719   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6720
6721   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6722   {
6723     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6724
6725     header[i * 2 + 0] = header_word >> 8;
6726     header[i * 2 + 1] = header_word & 0xff;
6727   }
6728
6729   // read some values from level header to check level decoding integrity
6730   fieldx = header[6] | (header[7] << 8);
6731   fieldy = header[8] | (header[9] << 8);
6732   num_yamyam_contents = header[60] | (header[61] << 8);
6733
6734   // do some simple sanity checks to ensure that level was correctly decoded
6735   if (fieldx < 1 || fieldx > 256 ||
6736       fieldy < 1 || fieldy > 256 ||
6737       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6738   {
6739     level->no_valid_file = TRUE;
6740
6741     Warn("cannot decode level from stream -- using empty level");
6742
6743     return;
6744   }
6745
6746   // maximum envelope header size is 31 bytes
6747   envelope_header_len   = header[envelope_header_pos];
6748   // maximum envelope content size is 110 (156?) bytes
6749   envelope_content_len  = header[envelope_content_pos];
6750
6751   // maximum level title size is 40 bytes
6752   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6753   // maximum level author size is 30 (51?) bytes
6754   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6755
6756   envelope_size = 0;
6757
6758   for (i = 0; i < envelope_header_len; i++)
6759     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6760       level->envelope[0].text[envelope_size++] =
6761         header[envelope_header_pos + 1 + i];
6762
6763   if (envelope_header_len > 0 && envelope_content_len > 0)
6764   {
6765     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6766       level->envelope[0].text[envelope_size++] = '\n';
6767     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6768       level->envelope[0].text[envelope_size++] = '\n';
6769   }
6770
6771   for (i = 0; i < envelope_content_len; i++)
6772     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6773       level->envelope[0].text[envelope_size++] =
6774         header[envelope_content_pos + 1 + i];
6775
6776   level->envelope[0].text[envelope_size] = '\0';
6777
6778   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6779   level->envelope[0].ysize = 10;
6780   level->envelope[0].autowrap = TRUE;
6781   level->envelope[0].centered = TRUE;
6782
6783   for (i = 0; i < level_name_len; i++)
6784     level->name[i] = header[level_name_pos + 1 + i];
6785   level->name[level_name_len] = '\0';
6786
6787   for (i = 0; i < level_author_len; i++)
6788     level->author[i] = header[level_author_pos + 1 + i];
6789   level->author[level_author_len] = '\0';
6790
6791   num_yamyam_contents = header[60] | (header[61] << 8);
6792   level->num_yamyam_contents =
6793     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6794
6795   for (i = 0; i < num_yamyam_contents; i++)
6796   {
6797     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6798     {
6799       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6800       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6801
6802       if (i < MAX_ELEMENT_CONTENTS)
6803         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6804     }
6805   }
6806
6807   fieldx = header[6] | (header[7] << 8);
6808   fieldy = header[8] | (header[9] << 8);
6809   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6810   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6811
6812   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6813   {
6814     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6815     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6816
6817     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6818       level->field[x][y] = getMappedElement_DC(element_dc);
6819   }
6820
6821   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6822   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6823   level->field[x][y] = EL_PLAYER_1;
6824
6825   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6826   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6827   level->field[x][y] = EL_PLAYER_2;
6828
6829   level->gems_needed            = header[18] | (header[19] << 8);
6830
6831   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6832   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6833   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6834   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6835   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6836   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6837   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6838   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6839   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6840   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6841   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6842   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6843
6844   level->time                   = header[44] | (header[45] << 8);
6845
6846   level->amoeba_speed           = header[46] | (header[47] << 8);
6847   level->time_light             = header[48] | (header[49] << 8);
6848   level->time_timegate          = header[50] | (header[51] << 8);
6849   level->time_wheel             = header[52] | (header[53] << 8);
6850   level->time_magic_wall        = header[54] | (header[55] << 8);
6851   level->extra_time             = header[56] | (header[57] << 8);
6852   level->shield_normal_time     = header[58] | (header[59] << 8);
6853
6854   // shield and extra time elements do not have a score
6855   level->score[SC_SHIELD]       = 0;
6856   level->extra_time_score       = 0;
6857
6858   // set time for normal and deadly shields to the same value
6859   level->shield_deadly_time     = level->shield_normal_time;
6860
6861   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6862   // can slip down from flat walls, like normal walls and steel walls
6863   level->em_slippery_gems = TRUE;
6864
6865   // time score is counted for each 10 seconds left in Diamond Caves levels
6866   level->time_score_base = 10;
6867 }
6868
6869 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6870                                      struct LevelFileInfo *level_file_info,
6871                                      boolean level_info_only)
6872 {
6873   char *filename = level_file_info->filename;
6874   File *file;
6875   int num_magic_bytes = 8;
6876   char magic_bytes[num_magic_bytes + 1];
6877   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6878
6879   if (!(file = openFile(filename, MODE_READ)))
6880   {
6881     level->no_valid_file = TRUE;
6882
6883     if (!level_info_only)
6884       Warn("cannot read level '%s' -- using empty level", filename);
6885
6886     return;
6887   }
6888
6889   // fseek(file, 0x0000, SEEK_SET);
6890
6891   if (level_file_info->packed)
6892   {
6893     // read "magic bytes" from start of file
6894     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6895       magic_bytes[0] = '\0';
6896
6897     // check "magic bytes" for correct file format
6898     if (!strPrefix(magic_bytes, "DC2"))
6899     {
6900       level->no_valid_file = TRUE;
6901
6902       Warn("unknown DC level file '%s' -- using empty level", filename);
6903
6904       return;
6905     }
6906
6907     if (strPrefix(magic_bytes, "DC2Win95") ||
6908         strPrefix(magic_bytes, "DC2Win98"))
6909     {
6910       int position_first_level = 0x00fa;
6911       int extra_bytes = 4;
6912       int skip_bytes;
6913
6914       // advance file stream to first level inside the level package
6915       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6916
6917       // each block of level data is followed by block of non-level data
6918       num_levels_to_skip *= 2;
6919
6920       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6921       while (num_levels_to_skip >= 0)
6922       {
6923         // advance file stream to next level inside the level package
6924         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6925         {
6926           level->no_valid_file = TRUE;
6927
6928           Warn("cannot fseek in file '%s' -- using empty level", filename);
6929
6930           return;
6931         }
6932
6933         // skip apparently unused extra bytes following each level
6934         ReadUnusedBytesFromFile(file, extra_bytes);
6935
6936         // read size of next level in level package
6937         skip_bytes = getFile32BitLE(file);
6938
6939         num_levels_to_skip--;
6940       }
6941     }
6942     else
6943     {
6944       level->no_valid_file = TRUE;
6945
6946       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6947
6948       return;
6949     }
6950   }
6951
6952   LoadLevelFromFileStream_DC(file, level);
6953
6954   closeFile(file);
6955 }
6956
6957
6958 // ----------------------------------------------------------------------------
6959 // functions for loading SB level
6960 // ----------------------------------------------------------------------------
6961
6962 int getMappedElement_SB(int element_ascii, boolean use_ces)
6963 {
6964   static struct
6965   {
6966     int ascii;
6967     int sb;
6968     int ce;
6969   }
6970   sb_element_mapping[] =
6971   {
6972     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6973     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6974     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6975     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6976     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6977     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6978     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6979     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6980
6981     { 0,   -1,                      -1          },
6982   };
6983
6984   int i;
6985
6986   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6987     if (element_ascii == sb_element_mapping[i].ascii)
6988       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6989
6990   return EL_UNDEFINED;
6991 }
6992
6993 static void SetLevelSettings_SB(struct LevelInfo *level)
6994 {
6995   // time settings
6996   level->time = 0;
6997   level->use_step_counter = TRUE;
6998
6999   // score settings
7000   level->score[SC_TIME_BONUS] = 0;
7001   level->time_score_base = 1;
7002   level->rate_time_over_score = TRUE;
7003
7004   // game settings
7005   level->auto_exit_sokoban = TRUE;
7006 }
7007
7008 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
7009                                      struct LevelFileInfo *level_file_info,
7010                                      boolean level_info_only)
7011 {
7012   char *filename = level_file_info->filename;
7013   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
7014   char last_comment[MAX_LINE_LEN];
7015   char level_name[MAX_LINE_LEN];
7016   char *line_ptr;
7017   File *file;
7018   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
7019   boolean read_continued_line = FALSE;
7020   boolean reading_playfield = FALSE;
7021   boolean got_valid_playfield_line = FALSE;
7022   boolean invalid_playfield_char = FALSE;
7023   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
7024   int file_level_nr = 0;
7025   int x = 0, y = 0;             // initialized to make compilers happy
7026
7027   last_comment[0] = '\0';
7028   level_name[0] = '\0';
7029
7030   if (!(file = openFile(filename, MODE_READ)))
7031   {
7032     level->no_valid_file = TRUE;
7033
7034     if (!level_info_only)
7035       Warn("cannot read level '%s' -- using empty level", filename);
7036
7037     return;
7038   }
7039
7040   while (!checkEndOfFile(file))
7041   {
7042     // level successfully read, but next level may follow here
7043     if (!got_valid_playfield_line && reading_playfield)
7044     {
7045       // read playfield from single level file -- skip remaining file
7046       if (!level_file_info->packed)
7047         break;
7048
7049       if (file_level_nr >= num_levels_to_skip)
7050         break;
7051
7052       file_level_nr++;
7053
7054       last_comment[0] = '\0';
7055       level_name[0] = '\0';
7056
7057       reading_playfield = FALSE;
7058     }
7059
7060     got_valid_playfield_line = FALSE;
7061
7062     // read next line of input file
7063     if (!getStringFromFile(file, line, MAX_LINE_LEN))
7064       break;
7065
7066     // cut trailing line break (this can be newline and/or carriage return)
7067     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
7068       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
7069         *line_ptr = '\0';
7070
7071     // copy raw input line for later use (mainly debugging output)
7072     strcpy(line_raw, line);
7073
7074     if (read_continued_line)
7075     {
7076       // append new line to existing line, if there is enough space
7077       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
7078         strcat(previous_line, line_ptr);
7079
7080       strcpy(line, previous_line);      // copy storage buffer to line
7081
7082       read_continued_line = FALSE;
7083     }
7084
7085     // if the last character is '\', continue at next line
7086     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
7087     {
7088       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
7089       strcpy(previous_line, line);      // copy line to storage buffer
7090
7091       read_continued_line = TRUE;
7092
7093       continue;
7094     }
7095
7096     // skip empty lines
7097     if (line[0] == '\0')
7098       continue;
7099
7100     // extract comment text from comment line
7101     if (line[0] == ';')
7102     {
7103       for (line_ptr = line; *line_ptr; line_ptr++)
7104         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
7105           break;
7106
7107       strcpy(last_comment, line_ptr);
7108
7109       continue;
7110     }
7111
7112     // extract level title text from line containing level title
7113     if (line[0] == '\'')
7114     {
7115       strcpy(level_name, &line[1]);
7116
7117       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
7118         level_name[strlen(level_name) - 1] = '\0';
7119
7120       continue;
7121     }
7122
7123     // skip lines containing only spaces (or empty lines)
7124     for (line_ptr = line; *line_ptr; line_ptr++)
7125       if (*line_ptr != ' ')
7126         break;
7127     if (*line_ptr == '\0')
7128       continue;
7129
7130     // at this point, we have found a line containing part of a playfield
7131
7132     got_valid_playfield_line = TRUE;
7133
7134     if (!reading_playfield)
7135     {
7136       reading_playfield = TRUE;
7137       invalid_playfield_char = FALSE;
7138
7139       for (x = 0; x < MAX_LEV_FIELDX; x++)
7140         for (y = 0; y < MAX_LEV_FIELDY; y++)
7141           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
7142
7143       level->fieldx = 0;
7144       level->fieldy = 0;
7145
7146       // start with topmost tile row
7147       y = 0;
7148     }
7149
7150     // skip playfield line if larger row than allowed
7151     if (y >= MAX_LEV_FIELDY)
7152       continue;
7153
7154     // start with leftmost tile column
7155     x = 0;
7156
7157     // read playfield elements from line
7158     for (line_ptr = line; *line_ptr; line_ptr++)
7159     {
7160       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7161
7162       // stop parsing playfield line if larger column than allowed
7163       if (x >= MAX_LEV_FIELDX)
7164         break;
7165
7166       if (mapped_sb_element == EL_UNDEFINED)
7167       {
7168         invalid_playfield_char = TRUE;
7169
7170         break;
7171       }
7172
7173       level->field[x][y] = mapped_sb_element;
7174
7175       // continue with next tile column
7176       x++;
7177
7178       level->fieldx = MAX(x, level->fieldx);
7179     }
7180
7181     if (invalid_playfield_char)
7182     {
7183       // if first playfield line, treat invalid lines as comment lines
7184       if (y == 0)
7185         reading_playfield = FALSE;
7186
7187       continue;
7188     }
7189
7190     // continue with next tile row
7191     y++;
7192   }
7193
7194   closeFile(file);
7195
7196   level->fieldy = y;
7197
7198   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7199   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7200
7201   if (!reading_playfield)
7202   {
7203     level->no_valid_file = TRUE;
7204
7205     Warn("cannot read level '%s' -- using empty level", filename);
7206
7207     return;
7208   }
7209
7210   if (*level_name != '\0')
7211   {
7212     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7213     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7214   }
7215   else if (*last_comment != '\0')
7216   {
7217     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7218     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7219   }
7220   else
7221   {
7222     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7223   }
7224
7225   // set all empty fields beyond the border walls to invisible steel wall
7226   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7227   {
7228     if ((x == 0 || x == level->fieldx - 1 ||
7229          y == 0 || y == level->fieldy - 1) &&
7230         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7231       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7232                      level->field, level->fieldx, level->fieldy);
7233   }
7234
7235   // set special level settings for Sokoban levels
7236   SetLevelSettings_SB(level);
7237
7238   if (load_xsb_to_ces)
7239   {
7240     // special global settings can now be set in level template
7241     level->use_custom_template = TRUE;
7242   }
7243 }
7244
7245
7246 // -------------------------------------------------------------------------
7247 // functions for handling native levels
7248 // -------------------------------------------------------------------------
7249
7250 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7251                                      struct LevelFileInfo *level_file_info,
7252                                      boolean level_info_only)
7253 {
7254   int pos = 0;
7255
7256   // determine position of requested level inside level package
7257   if (level_file_info->packed)
7258     pos = level_file_info->nr - leveldir_current->first_level;
7259
7260   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7261     level->no_valid_file = TRUE;
7262 }
7263
7264 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7265                                      struct LevelFileInfo *level_file_info,
7266                                      boolean level_info_only)
7267 {
7268   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7269     level->no_valid_file = TRUE;
7270 }
7271
7272 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7273                                      struct LevelFileInfo *level_file_info,
7274                                      boolean level_info_only)
7275 {
7276   int pos = 0;
7277
7278   // determine position of requested level inside level package
7279   if (level_file_info->packed)
7280     pos = level_file_info->nr - leveldir_current->first_level;
7281
7282   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7283     level->no_valid_file = TRUE;
7284 }
7285
7286 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7287                                      struct LevelFileInfo *level_file_info,
7288                                      boolean level_info_only)
7289 {
7290   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7291     level->no_valid_file = TRUE;
7292 }
7293
7294 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7295 {
7296   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7297     CopyNativeLevel_RND_to_BD(level);
7298   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7299     CopyNativeLevel_RND_to_EM(level);
7300   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7301     CopyNativeLevel_RND_to_SP(level);
7302   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7303     CopyNativeLevel_RND_to_MM(level);
7304 }
7305
7306 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7307 {
7308   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7309     CopyNativeLevel_BD_to_RND(level);
7310   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7311     CopyNativeLevel_EM_to_RND(level);
7312   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7313     CopyNativeLevel_SP_to_RND(level);
7314   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7315     CopyNativeLevel_MM_to_RND(level);
7316 }
7317
7318 void SaveNativeLevel(struct LevelInfo *level)
7319 {
7320   // saving native level files only supported for some game engines
7321   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7322       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7323     return;
7324
7325   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7326                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7327   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7328   char *filename = getLevelFilenameFromBasename(basename);
7329
7330   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7331     return;
7332
7333   boolean success = FALSE;
7334
7335   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7336   {
7337     CopyNativeLevel_RND_to_BD(level);
7338     // CopyNativeTape_RND_to_BD(level);
7339
7340     success = SaveNativeLevel_BD(filename);
7341   }
7342   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7343   {
7344     CopyNativeLevel_RND_to_SP(level);
7345     CopyNativeTape_RND_to_SP(level);
7346
7347     success = SaveNativeLevel_SP(filename);
7348   }
7349
7350   if (success)
7351     Request("Native level file saved!", REQ_CONFIRM);
7352   else
7353     Request("Failed to save native level file!", REQ_CONFIRM);
7354 }
7355
7356
7357 // ----------------------------------------------------------------------------
7358 // functions for loading generic level
7359 // ----------------------------------------------------------------------------
7360
7361 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7362                                   struct LevelFileInfo *level_file_info,
7363                                   boolean level_info_only)
7364 {
7365   // always start with reliable default values
7366   setLevelInfoToDefaults(level, level_info_only, TRUE);
7367
7368   switch (level_file_info->type)
7369   {
7370     case LEVEL_FILE_TYPE_RND:
7371       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7372       break;
7373
7374     case LEVEL_FILE_TYPE_BD:
7375       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7376       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7377       break;
7378
7379     case LEVEL_FILE_TYPE_EM:
7380       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7381       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7382       break;
7383
7384     case LEVEL_FILE_TYPE_SP:
7385       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7386       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7387       break;
7388
7389     case LEVEL_FILE_TYPE_MM:
7390       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7391       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7392       break;
7393
7394     case LEVEL_FILE_TYPE_DC:
7395       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7396       break;
7397
7398     case LEVEL_FILE_TYPE_SB:
7399       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7400       break;
7401
7402     default:
7403       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7404       break;
7405   }
7406
7407   // if level file is invalid, restore level structure to default values
7408   if (level->no_valid_file)
7409     setLevelInfoToDefaults(level, level_info_only, FALSE);
7410
7411   if (check_special_flags("use_native_bd_game_engine"))
7412     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7413
7414   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7415     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7416
7417   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7418     CopyNativeLevel_Native_to_RND(level);
7419 }
7420
7421 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7422 {
7423   static struct LevelFileInfo level_file_info;
7424
7425   // always start with reliable default values
7426   setFileInfoToDefaults(&level_file_info);
7427
7428   level_file_info.nr = 0;                       // unknown level number
7429   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7430
7431   setString(&level_file_info.filename, filename);
7432
7433   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7434 }
7435
7436 static void LoadLevel_InitVersion(struct LevelInfo *level)
7437 {
7438   int i, j;
7439
7440   if (leveldir_current == NULL)         // only when dumping level
7441     return;
7442
7443   // all engine modifications also valid for levels which use latest engine
7444   if (level->game_version < VERSION_IDENT(3,2,0,5))
7445   {
7446     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7447     level->time_score_base = 10;
7448   }
7449
7450   if (leveldir_current->latest_engine)
7451   {
7452     // ---------- use latest game engine --------------------------------------
7453
7454     /* For all levels which are forced to use the latest game engine version
7455        (normally all but user contributed, private and undefined levels), set
7456        the game engine version to the actual version; this allows for actual
7457        corrections in the game engine to take effect for existing, converted
7458        levels (from "classic" or other existing games) to make the emulation
7459        of the corresponding game more accurate, while (hopefully) not breaking
7460        existing levels created from other players. */
7461
7462     level->game_version = GAME_VERSION_ACTUAL;
7463
7464     /* Set special EM style gems behaviour: EM style gems slip down from
7465        normal, steel and growing wall. As this is a more fundamental change,
7466        it seems better to set the default behaviour to "off" (as it is more
7467        natural) and make it configurable in the level editor (as a property
7468        of gem style elements). Already existing converted levels (neither
7469        private nor contributed levels) are changed to the new behaviour. */
7470
7471     if (level->file_version < FILE_VERSION_2_0)
7472       level->em_slippery_gems = TRUE;
7473
7474     return;
7475   }
7476
7477   // ---------- use game engine the level was created with --------------------
7478
7479   /* For all levels which are not forced to use the latest game engine
7480      version (normally user contributed, private and undefined levels),
7481      use the version of the game engine the levels were created for.
7482
7483      Since 2.0.1, the game engine version is now directly stored
7484      in the level file (chunk "VERS"), so there is no need anymore
7485      to set the game version from the file version (except for old,
7486      pre-2.0 levels, where the game version is still taken from the
7487      file format version used to store the level -- see above). */
7488
7489   // player was faster than enemies in 1.0.0 and before
7490   if (level->file_version == FILE_VERSION_1_0)
7491     for (i = 0; i < MAX_PLAYERS; i++)
7492       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7493
7494   // default behaviour for EM style gems was "slippery" only in 2.0.1
7495   if (level->game_version == VERSION_IDENT(2,0,1,0))
7496     level->em_slippery_gems = TRUE;
7497
7498   // springs could be pushed over pits before (pre-release version) 2.2.0
7499   if (level->game_version < VERSION_IDENT(2,2,0,0))
7500     level->use_spring_bug = TRUE;
7501
7502   if (level->game_version < VERSION_IDENT(3,2,0,5))
7503   {
7504     // time orb caused limited time in endless time levels before 3.2.0-5
7505     level->use_time_orb_bug = TRUE;
7506
7507     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7508     level->block_snap_field = FALSE;
7509
7510     // extra time score was same value as time left score before 3.2.0-5
7511     level->extra_time_score = level->score[SC_TIME_BONUS];
7512   }
7513
7514   if (level->game_version < VERSION_IDENT(3,2,0,7))
7515   {
7516     // default behaviour for snapping was "not continuous" before 3.2.0-7
7517     level->continuous_snapping = FALSE;
7518   }
7519
7520   // only few elements were able to actively move into acid before 3.1.0
7521   // trigger settings did not exist before 3.1.0; set to default "any"
7522   if (level->game_version < VERSION_IDENT(3,1,0,0))
7523   {
7524     // correct "can move into acid" settings (all zero in old levels)
7525
7526     level->can_move_into_acid_bits = 0; // nothing can move into acid
7527     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7528
7529     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7530     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7531     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7532     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7533
7534     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7535       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7536
7537     // correct trigger settings (stored as zero == "none" in old levels)
7538
7539     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7540     {
7541       int element = EL_CUSTOM_START + i;
7542       struct ElementInfo *ei = &element_info[element];
7543
7544       for (j = 0; j < ei->num_change_pages; j++)
7545       {
7546         struct ElementChangeInfo *change = &ei->change_page[j];
7547
7548         change->trigger_player = CH_PLAYER_ANY;
7549         change->trigger_page = CH_PAGE_ANY;
7550       }
7551     }
7552   }
7553
7554   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7555   {
7556     int element = EL_CUSTOM_256;
7557     struct ElementInfo *ei = &element_info[element];
7558     struct ElementChangeInfo *change = &ei->change_page[0];
7559
7560     /* This is needed to fix a problem that was caused by a bugfix in function
7561        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7562        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7563        not replace walkable elements, but instead just placed the player on it,
7564        without placing the Sokoban field under the player). Unfortunately, this
7565        breaks "Snake Bite" style levels when the snake is halfway through a door
7566        that just closes (the snake head is still alive and can be moved in this
7567        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7568        player (without Sokoban element) which then gets killed as designed). */
7569
7570     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7571          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7572         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7573       change->target_element = EL_PLAYER_1;
7574   }
7575
7576   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7577   if (level->game_version < VERSION_IDENT(3,2,5,0))
7578   {
7579     /* This is needed to fix a problem that was caused by a bugfix in function
7580        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7581        corrects the behaviour when a custom element changes to another custom
7582        element with a higher element number that has change actions defined.
7583        Normally, only one change per frame is allowed for custom elements.
7584        Therefore, it is checked if a custom element already changed in the
7585        current frame; if it did, subsequent changes are suppressed.
7586        Unfortunately, this is only checked for element changes, but not for
7587        change actions, which are still executed. As the function above loops
7588        through all custom elements from lower to higher, an element change
7589        resulting in a lower CE number won't be checked again, while a target
7590        element with a higher number will also be checked, and potential change
7591        actions will get executed for this CE, too (which is wrong), while
7592        further changes are ignored (which is correct). As this bugfix breaks
7593        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7594        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7595        behaviour for existing levels and tapes that make use of this bug */
7596
7597     level->use_action_after_change_bug = TRUE;
7598   }
7599
7600   // not centering level after relocating player was default only in 3.2.3
7601   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7602     level->shifted_relocation = TRUE;
7603
7604   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7605   if (level->game_version < VERSION_IDENT(3,2,6,0))
7606     level->em_explodes_by_fire = TRUE;
7607
7608   // levels were solved by the first player entering an exit up to 4.1.0.0
7609   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7610     level->solved_by_one_player = TRUE;
7611
7612   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7613   if (level->game_version < VERSION_IDENT(4,1,1,1))
7614     level->use_life_bugs = TRUE;
7615
7616   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7617   if (level->game_version < VERSION_IDENT(4,1,1,1))
7618     level->sb_objects_needed = FALSE;
7619
7620   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7621   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7622     level->finish_dig_collect = FALSE;
7623
7624   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7625   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7626     level->keep_walkable_ce = TRUE;
7627 }
7628
7629 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7630 {
7631   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7632   int x, y;
7633
7634   // check if this level is (not) a Sokoban level
7635   for (y = 0; y < level->fieldy; y++)
7636     for (x = 0; x < level->fieldx; x++)
7637       if (!IS_SB_ELEMENT(Tile[x][y]))
7638         is_sokoban_level = FALSE;
7639
7640   if (is_sokoban_level)
7641   {
7642     // set special level settings for Sokoban levels
7643     SetLevelSettings_SB(level);
7644   }
7645 }
7646
7647 static void LoadLevel_InitSettings(struct LevelInfo *level)
7648 {
7649   // adjust level settings for (non-native) Sokoban-style levels
7650   LoadLevel_InitSettings_SB(level);
7651
7652   // rename levels with title "nameless level" or if renaming is forced
7653   if (leveldir_current->empty_level_name != NULL &&
7654       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7655        leveldir_current->force_level_name))
7656     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7657              leveldir_current->empty_level_name, level_nr);
7658 }
7659
7660 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7661 {
7662   int i, x, y;
7663
7664   // map elements that have changed in newer versions
7665   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7666                                                     level->game_version);
7667   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7668     for (x = 0; x < 3; x++)
7669       for (y = 0; y < 3; y++)
7670         level->yamyam_content[i].e[x][y] =
7671           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7672                                     level->game_version);
7673
7674 }
7675
7676 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7677 {
7678   int i, j;
7679
7680   // map custom element change events that have changed in newer versions
7681   // (these following values were accidentally changed in version 3.0.1)
7682   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7683   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7684   {
7685     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7686     {
7687       int element = EL_CUSTOM_START + i;
7688
7689       // order of checking and copying events to be mapped is important
7690       // (do not change the start and end value -- they are constant)
7691       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7692       {
7693         if (HAS_CHANGE_EVENT(element, j - 2))
7694         {
7695           SET_CHANGE_EVENT(element, j - 2, FALSE);
7696           SET_CHANGE_EVENT(element, j, TRUE);
7697         }
7698       }
7699
7700       // order of checking and copying events to be mapped is important
7701       // (do not change the start and end value -- they are constant)
7702       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7703       {
7704         if (HAS_CHANGE_EVENT(element, j - 1))
7705         {
7706           SET_CHANGE_EVENT(element, j - 1, FALSE);
7707           SET_CHANGE_EVENT(element, j, TRUE);
7708         }
7709       }
7710     }
7711   }
7712
7713   // initialize "can_change" field for old levels with only one change page
7714   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7715   {
7716     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7717     {
7718       int element = EL_CUSTOM_START + i;
7719
7720       if (CAN_CHANGE(element))
7721         element_info[element].change->can_change = TRUE;
7722     }
7723   }
7724
7725   // correct custom element values (for old levels without these options)
7726   if (level->game_version < VERSION_IDENT(3,1,1,0))
7727   {
7728     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7729     {
7730       int element = EL_CUSTOM_START + i;
7731       struct ElementInfo *ei = &element_info[element];
7732
7733       if (ei->access_direction == MV_NO_DIRECTION)
7734         ei->access_direction = MV_ALL_DIRECTIONS;
7735     }
7736   }
7737
7738   // correct custom element values (fix invalid values for all versions)
7739   if (1)
7740   {
7741     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7742     {
7743       int element = EL_CUSTOM_START + i;
7744       struct ElementInfo *ei = &element_info[element];
7745
7746       for (j = 0; j < ei->num_change_pages; j++)
7747       {
7748         struct ElementChangeInfo *change = &ei->change_page[j];
7749
7750         if (change->trigger_player == CH_PLAYER_NONE)
7751           change->trigger_player = CH_PLAYER_ANY;
7752
7753         if (change->trigger_side == CH_SIDE_NONE)
7754           change->trigger_side = CH_SIDE_ANY;
7755       }
7756     }
7757   }
7758
7759   // initialize "can_explode" field for old levels which did not store this
7760   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7761   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7762   {
7763     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7764     {
7765       int element = EL_CUSTOM_START + i;
7766
7767       if (EXPLODES_1X1_OLD(element))
7768         element_info[element].explosion_type = EXPLODES_1X1;
7769
7770       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7771                                              EXPLODES_SMASHED(element) ||
7772                                              EXPLODES_IMPACT(element)));
7773     }
7774   }
7775
7776   // correct previously hard-coded move delay values for maze runner style
7777   if (level->game_version < VERSION_IDENT(3,1,1,0))
7778   {
7779     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7780     {
7781       int element = EL_CUSTOM_START + i;
7782
7783       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7784       {
7785         // previously hard-coded and therefore ignored
7786         element_info[element].move_delay_fixed = 9;
7787         element_info[element].move_delay_random = 0;
7788       }
7789     }
7790   }
7791
7792   // set some other uninitialized values of custom elements in older levels
7793   if (level->game_version < VERSION_IDENT(3,1,0,0))
7794   {
7795     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7796     {
7797       int element = EL_CUSTOM_START + i;
7798
7799       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7800
7801       element_info[element].explosion_delay = 17;
7802       element_info[element].ignition_delay = 8;
7803     }
7804   }
7805
7806   // set mouse click change events to work for left/middle/right mouse button
7807   if (level->game_version < VERSION_IDENT(4,2,3,0))
7808   {
7809     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7810     {
7811       int element = EL_CUSTOM_START + i;
7812       struct ElementInfo *ei = &element_info[element];
7813
7814       for (j = 0; j < ei->num_change_pages; j++)
7815       {
7816         struct ElementChangeInfo *change = &ei->change_page[j];
7817
7818         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7819             change->has_event[CE_PRESSED_BY_MOUSE] ||
7820             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7821             change->has_event[CE_MOUSE_PRESSED_ON_X])
7822           change->trigger_side = CH_SIDE_ANY;
7823       }
7824     }
7825   }
7826 }
7827
7828 static void LoadLevel_InitElements(struct LevelInfo *level)
7829 {
7830   LoadLevel_InitStandardElements(level);
7831
7832   if (level->file_has_custom_elements)
7833     LoadLevel_InitCustomElements(level);
7834
7835   // initialize element properties for level editor etc.
7836   InitElementPropertiesEngine(level->game_version);
7837   InitElementPropertiesGfxElement();
7838 }
7839
7840 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7841 {
7842   int x, y;
7843
7844   // map elements that have changed in newer versions
7845   for (y = 0; y < level->fieldy; y++)
7846     for (x = 0; x < level->fieldx; x++)
7847       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7848                                                      level->game_version);
7849
7850   // clear unused playfield data (nicer if level gets resized in editor)
7851   for (x = 0; x < MAX_LEV_FIELDX; x++)
7852     for (y = 0; y < MAX_LEV_FIELDY; y++)
7853       if (x >= level->fieldx || y >= level->fieldy)
7854         level->field[x][y] = EL_EMPTY;
7855
7856   // copy elements to runtime playfield array
7857   for (x = 0; x < MAX_LEV_FIELDX; x++)
7858     for (y = 0; y < MAX_LEV_FIELDY; y++)
7859       Tile[x][y] = level->field[x][y];
7860
7861   // initialize level size variables for faster access
7862   lev_fieldx = level->fieldx;
7863   lev_fieldy = level->fieldy;
7864
7865   // determine border element for this level
7866   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7867     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7868   else
7869     SetBorderElement();
7870 }
7871
7872 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7873 {
7874   struct LevelFileInfo *level_file_info = &level->file_info;
7875
7876   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7877     CopyNativeLevel_RND_to_Native(level);
7878 }
7879
7880 static void LoadLevelTemplate_LoadAndInit(void)
7881 {
7882   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7883
7884   LoadLevel_InitVersion(&level_template);
7885   LoadLevel_InitElements(&level_template);
7886   LoadLevel_InitSettings(&level_template);
7887
7888   ActivateLevelTemplate();
7889 }
7890
7891 void LoadLevelTemplate(int nr)
7892 {
7893   if (!fileExists(getGlobalLevelTemplateFilename()))
7894   {
7895     Warn("no level template found for this level");
7896
7897     return;
7898   }
7899
7900   setLevelFileInfo(&level_template.file_info, nr);
7901
7902   LoadLevelTemplate_LoadAndInit();
7903 }
7904
7905 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7906 {
7907   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7908
7909   LoadLevelTemplate_LoadAndInit();
7910 }
7911
7912 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7913 {
7914   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7915
7916   if (level.use_custom_template)
7917   {
7918     if (network_level != NULL)
7919       LoadNetworkLevelTemplate(network_level);
7920     else
7921       LoadLevelTemplate(-1);
7922   }
7923
7924   LoadLevel_InitVersion(&level);
7925   LoadLevel_InitElements(&level);
7926   LoadLevel_InitPlayfield(&level);
7927   LoadLevel_InitSettings(&level);
7928
7929   LoadLevel_InitNativeEngines(&level);
7930 }
7931
7932 void LoadLevel(int nr)
7933 {
7934   SetLevelSetInfo(leveldir_current->identifier, nr);
7935
7936   setLevelFileInfo(&level.file_info, nr);
7937
7938   LoadLevel_LoadAndInit(NULL);
7939 }
7940
7941 void LoadLevelInfoOnly(int nr)
7942 {
7943   setLevelFileInfo(&level.file_info, nr);
7944
7945   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7946 }
7947
7948 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7949 {
7950   SetLevelSetInfo(network_level->leveldir_identifier,
7951                   network_level->file_info.nr);
7952
7953   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7954
7955   LoadLevel_LoadAndInit(network_level);
7956 }
7957
7958 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7959 {
7960   int chunk_size = 0;
7961
7962   chunk_size += putFileVersion(file, level->file_version);
7963   chunk_size += putFileVersion(file, level->game_version);
7964
7965   return chunk_size;
7966 }
7967
7968 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7969 {
7970   int chunk_size = 0;
7971
7972   chunk_size += putFile16BitBE(file, level->creation_date.year);
7973   chunk_size += putFile8Bit(file,    level->creation_date.month);
7974   chunk_size += putFile8Bit(file,    level->creation_date.day);
7975
7976   return chunk_size;
7977 }
7978
7979 #if ENABLE_HISTORIC_CHUNKS
7980 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7981 {
7982   int i, x, y;
7983
7984   putFile8Bit(file, level->fieldx);
7985   putFile8Bit(file, level->fieldy);
7986
7987   putFile16BitBE(file, level->time);
7988   putFile16BitBE(file, level->gems_needed);
7989
7990   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7991     putFile8Bit(file, level->name[i]);
7992
7993   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7994     putFile8Bit(file, level->score[i]);
7995
7996   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7997     for (y = 0; y < 3; y++)
7998       for (x = 0; x < 3; x++)
7999         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
8000                            level->yamyam_content[i].e[x][y]));
8001   putFile8Bit(file, level->amoeba_speed);
8002   putFile8Bit(file, level->time_magic_wall);
8003   putFile8Bit(file, level->time_wheel);
8004   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
8005                      level->amoeba_content));
8006   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
8007   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
8008   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
8009   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
8010
8011   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
8012
8013   putFile8Bit(file, (level->block_last_field ? 1 : 0));
8014   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
8015   putFile32BitBE(file, level->can_move_into_acid_bits);
8016   putFile8Bit(file, level->dont_collide_with_bits);
8017
8018   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
8019   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
8020
8021   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
8022   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
8023   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
8024
8025   putFile8Bit(file, level->game_engine_type);
8026
8027   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
8028 }
8029 #endif
8030
8031 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
8032 {
8033   int chunk_size = 0;
8034   int i;
8035
8036   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
8037     chunk_size += putFile8Bit(file, level->name[i]);
8038
8039   return chunk_size;
8040 }
8041
8042 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
8043 {
8044   int chunk_size = 0;
8045   int i;
8046
8047   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
8048     chunk_size += putFile8Bit(file, level->author[i]);
8049
8050   return chunk_size;
8051 }
8052
8053 #if ENABLE_HISTORIC_CHUNKS
8054 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8055 {
8056   int chunk_size = 0;
8057   int x, y;
8058
8059   for (y = 0; y < level->fieldy; y++)
8060     for (x = 0; x < level->fieldx; x++)
8061       if (level->encoding_16bit_field)
8062         chunk_size += putFile16BitBE(file, level->field[x][y]);
8063       else
8064         chunk_size += putFile8Bit(file, level->field[x][y]);
8065
8066   return chunk_size;
8067 }
8068 #endif
8069
8070 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
8071 {
8072   int chunk_size = 0;
8073   int x, y;
8074
8075   for (y = 0; y < level->fieldy; y++) 
8076     for (x = 0; x < level->fieldx; x++) 
8077       chunk_size += putFile16BitBE(file, level->field[x][y]);
8078
8079   return chunk_size;
8080 }
8081
8082 #if ENABLE_HISTORIC_CHUNKS
8083 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
8084 {
8085   int i, x, y;
8086
8087   putFile8Bit(file, EL_YAMYAM);
8088   putFile8Bit(file, level->num_yamyam_contents);
8089   putFile8Bit(file, 0);
8090   putFile8Bit(file, 0);
8091
8092   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8093     for (y = 0; y < 3; y++)
8094       for (x = 0; x < 3; x++)
8095         if (level->encoding_16bit_field)
8096           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
8097         else
8098           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
8099 }
8100 #endif
8101
8102 #if ENABLE_HISTORIC_CHUNKS
8103 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
8104 {
8105   int i, x, y;
8106   int num_contents, content_xsize, content_ysize;
8107   int content_array[MAX_ELEMENT_CONTENTS][3][3];
8108
8109   if (element == EL_YAMYAM)
8110   {
8111     num_contents = level->num_yamyam_contents;
8112     content_xsize = 3;
8113     content_ysize = 3;
8114
8115     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8116       for (y = 0; y < 3; y++)
8117         for (x = 0; x < 3; x++)
8118           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
8119   }
8120   else if (element == EL_BD_AMOEBA)
8121   {
8122     num_contents = 1;
8123     content_xsize = 1;
8124     content_ysize = 1;
8125
8126     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8127       for (y = 0; y < 3; y++)
8128         for (x = 0; x < 3; x++)
8129           content_array[i][x][y] = EL_EMPTY;
8130     content_array[0][0][0] = level->amoeba_content;
8131   }
8132   else
8133   {
8134     // chunk header already written -- write empty chunk data
8135     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
8136
8137     Warn("cannot save content for element '%d'", element);
8138
8139     return;
8140   }
8141
8142   putFile16BitBE(file, element);
8143   putFile8Bit(file, num_contents);
8144   putFile8Bit(file, content_xsize);
8145   putFile8Bit(file, content_ysize);
8146
8147   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
8148
8149   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
8150     for (y = 0; y < 3; y++)
8151       for (x = 0; x < 3; x++)
8152         putFile16BitBE(file, content_array[i][x][y]);
8153 }
8154 #endif
8155
8156 #if ENABLE_HISTORIC_CHUNKS
8157 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8158 {
8159   int envelope_nr = element - EL_ENVELOPE_1;
8160   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8161   int chunk_size = 0;
8162   int i;
8163
8164   chunk_size += putFile16BitBE(file, element);
8165   chunk_size += putFile16BitBE(file, envelope_len);
8166   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8167   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8168
8169   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8170   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8171
8172   for (i = 0; i < envelope_len; i++)
8173     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8174
8175   return chunk_size;
8176 }
8177 #endif
8178
8179 #if ENABLE_HISTORIC_CHUNKS
8180 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8181                            int num_changed_custom_elements)
8182 {
8183   int i, check = 0;
8184
8185   putFile16BitBE(file, num_changed_custom_elements);
8186
8187   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8188   {
8189     int element = EL_CUSTOM_START + i;
8190
8191     struct ElementInfo *ei = &element_info[element];
8192
8193     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8194     {
8195       if (check < num_changed_custom_elements)
8196       {
8197         putFile16BitBE(file, element);
8198         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8199       }
8200
8201       check++;
8202     }
8203   }
8204
8205   if (check != num_changed_custom_elements)     // should not happen
8206     Warn("inconsistent number of custom element properties");
8207 }
8208 #endif
8209
8210 #if ENABLE_HISTORIC_CHUNKS
8211 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8212                            int num_changed_custom_elements)
8213 {
8214   int i, check = 0;
8215
8216   putFile16BitBE(file, num_changed_custom_elements);
8217
8218   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8219   {
8220     int element = EL_CUSTOM_START + i;
8221
8222     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8223     {
8224       if (check < num_changed_custom_elements)
8225       {
8226         putFile16BitBE(file, element);
8227         putFile16BitBE(file, element_info[element].change->target_element);
8228       }
8229
8230       check++;
8231     }
8232   }
8233
8234   if (check != num_changed_custom_elements)     // should not happen
8235     Warn("inconsistent number of custom target elements");
8236 }
8237 #endif
8238
8239 #if ENABLE_HISTORIC_CHUNKS
8240 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8241                            int num_changed_custom_elements)
8242 {
8243   int i, j, x, y, check = 0;
8244
8245   putFile16BitBE(file, num_changed_custom_elements);
8246
8247   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8248   {
8249     int element = EL_CUSTOM_START + i;
8250     struct ElementInfo *ei = &element_info[element];
8251
8252     if (ei->modified_settings)
8253     {
8254       if (check < num_changed_custom_elements)
8255       {
8256         putFile16BitBE(file, element);
8257
8258         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8259           putFile8Bit(file, ei->description[j]);
8260
8261         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8262
8263         // some free bytes for future properties and padding
8264         WriteUnusedBytesToFile(file, 7);
8265
8266         putFile8Bit(file, ei->use_gfx_element);
8267         putFile16BitBE(file, ei->gfx_element_initial);
8268
8269         putFile8Bit(file, ei->collect_score_initial);
8270         putFile8Bit(file, ei->collect_count_initial);
8271
8272         putFile16BitBE(file, ei->push_delay_fixed);
8273         putFile16BitBE(file, ei->push_delay_random);
8274         putFile16BitBE(file, ei->move_delay_fixed);
8275         putFile16BitBE(file, ei->move_delay_random);
8276
8277         putFile16BitBE(file, ei->move_pattern);
8278         putFile8Bit(file, ei->move_direction_initial);
8279         putFile8Bit(file, ei->move_stepsize);
8280
8281         for (y = 0; y < 3; y++)
8282           for (x = 0; x < 3; x++)
8283             putFile16BitBE(file, ei->content.e[x][y]);
8284
8285         putFile32BitBE(file, ei->change->events);
8286
8287         putFile16BitBE(file, ei->change->target_element);
8288
8289         putFile16BitBE(file, ei->change->delay_fixed);
8290         putFile16BitBE(file, ei->change->delay_random);
8291         putFile16BitBE(file, ei->change->delay_frames);
8292
8293         putFile16BitBE(file, ei->change->initial_trigger_element);
8294
8295         putFile8Bit(file, ei->change->explode);
8296         putFile8Bit(file, ei->change->use_target_content);
8297         putFile8Bit(file, ei->change->only_if_complete);
8298         putFile8Bit(file, ei->change->use_random_replace);
8299
8300         putFile8Bit(file, ei->change->random_percentage);
8301         putFile8Bit(file, ei->change->replace_when);
8302
8303         for (y = 0; y < 3; y++)
8304           for (x = 0; x < 3; x++)
8305             putFile16BitBE(file, ei->change->content.e[x][y]);
8306
8307         putFile8Bit(file, ei->slippery_type);
8308
8309         // some free bytes for future properties and padding
8310         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8311       }
8312
8313       check++;
8314     }
8315   }
8316
8317   if (check != num_changed_custom_elements)     // should not happen
8318     Warn("inconsistent number of custom element properties");
8319 }
8320 #endif
8321
8322 #if ENABLE_HISTORIC_CHUNKS
8323 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8324 {
8325   struct ElementInfo *ei = &element_info[element];
8326   int i, j, x, y;
8327
8328   // ---------- custom element base property values (96 bytes) ----------------
8329
8330   putFile16BitBE(file, element);
8331
8332   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8333     putFile8Bit(file, ei->description[i]);
8334
8335   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8336
8337   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8338
8339   putFile8Bit(file, ei->num_change_pages);
8340
8341   putFile16BitBE(file, ei->ce_value_fixed_initial);
8342   putFile16BitBE(file, ei->ce_value_random_initial);
8343   putFile8Bit(file, ei->use_last_ce_value);
8344
8345   putFile8Bit(file, ei->use_gfx_element);
8346   putFile16BitBE(file, ei->gfx_element_initial);
8347
8348   putFile8Bit(file, ei->collect_score_initial);
8349   putFile8Bit(file, ei->collect_count_initial);
8350
8351   putFile8Bit(file, ei->drop_delay_fixed);
8352   putFile8Bit(file, ei->push_delay_fixed);
8353   putFile8Bit(file, ei->drop_delay_random);
8354   putFile8Bit(file, ei->push_delay_random);
8355   putFile16BitBE(file, ei->move_delay_fixed);
8356   putFile16BitBE(file, ei->move_delay_random);
8357
8358   // bits 0 - 15 of "move_pattern" ...
8359   putFile16BitBE(file, ei->move_pattern & 0xffff);
8360   putFile8Bit(file, ei->move_direction_initial);
8361   putFile8Bit(file, ei->move_stepsize);
8362
8363   putFile8Bit(file, ei->slippery_type);
8364
8365   for (y = 0; y < 3; y++)
8366     for (x = 0; x < 3; x++)
8367       putFile16BitBE(file, ei->content.e[x][y]);
8368
8369   putFile16BitBE(file, ei->move_enter_element);
8370   putFile16BitBE(file, ei->move_leave_element);
8371   putFile8Bit(file, ei->move_leave_type);
8372
8373   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8374   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8375
8376   putFile8Bit(file, ei->access_direction);
8377
8378   putFile8Bit(file, ei->explosion_delay);
8379   putFile8Bit(file, ei->ignition_delay);
8380   putFile8Bit(file, ei->explosion_type);
8381
8382   // some free bytes for future custom property values and padding
8383   WriteUnusedBytesToFile(file, 1);
8384
8385   // ---------- change page property values (48 bytes) ------------------------
8386
8387   for (i = 0; i < ei->num_change_pages; i++)
8388   {
8389     struct ElementChangeInfo *change = &ei->change_page[i];
8390     unsigned int event_bits;
8391
8392     // bits 0 - 31 of "has_event[]" ...
8393     event_bits = 0;
8394     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8395       if (change->has_event[j])
8396         event_bits |= (1u << j);
8397     putFile32BitBE(file, event_bits);
8398
8399     putFile16BitBE(file, change->target_element);
8400
8401     putFile16BitBE(file, change->delay_fixed);
8402     putFile16BitBE(file, change->delay_random);
8403     putFile16BitBE(file, change->delay_frames);
8404
8405     putFile16BitBE(file, change->initial_trigger_element);
8406
8407     putFile8Bit(file, change->explode);
8408     putFile8Bit(file, change->use_target_content);
8409     putFile8Bit(file, change->only_if_complete);
8410     putFile8Bit(file, change->use_random_replace);
8411
8412     putFile8Bit(file, change->random_percentage);
8413     putFile8Bit(file, change->replace_when);
8414
8415     for (y = 0; y < 3; y++)
8416       for (x = 0; x < 3; x++)
8417         putFile16BitBE(file, change->target_content.e[x][y]);
8418
8419     putFile8Bit(file, change->can_change);
8420
8421     putFile8Bit(file, change->trigger_side);
8422
8423     putFile8Bit(file, change->trigger_player);
8424     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8425                        log_2(change->trigger_page)));
8426
8427     putFile8Bit(file, change->has_action);
8428     putFile8Bit(file, change->action_type);
8429     putFile8Bit(file, change->action_mode);
8430     putFile16BitBE(file, change->action_arg);
8431
8432     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8433     event_bits = 0;
8434     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8435       if (change->has_event[j])
8436         event_bits |= (1u << (j - 32));
8437     putFile8Bit(file, event_bits);
8438   }
8439 }
8440 #endif
8441
8442 #if ENABLE_HISTORIC_CHUNKS
8443 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8444 {
8445   struct ElementInfo *ei = &element_info[element];
8446   struct ElementGroupInfo *group = ei->group;
8447   int i;
8448
8449   putFile16BitBE(file, element);
8450
8451   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8452     putFile8Bit(file, ei->description[i]);
8453
8454   putFile8Bit(file, group->num_elements);
8455
8456   putFile8Bit(file, ei->use_gfx_element);
8457   putFile16BitBE(file, ei->gfx_element_initial);
8458
8459   putFile8Bit(file, group->choice_mode);
8460
8461   // some free bytes for future values and padding
8462   WriteUnusedBytesToFile(file, 3);
8463
8464   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8465     putFile16BitBE(file, group->element[i]);
8466 }
8467 #endif
8468
8469 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8470                                 boolean write_element)
8471 {
8472   int save_type = entry->save_type;
8473   int data_type = entry->data_type;
8474   int conf_type = entry->conf_type;
8475   int byte_mask = conf_type & CONF_MASK_BYTES;
8476   int element = entry->element;
8477   int default_value = entry->default_value;
8478   int num_bytes = 0;
8479   boolean modified = FALSE;
8480
8481   if (byte_mask != CONF_MASK_MULTI_BYTES)
8482   {
8483     void *value_ptr = entry->value;
8484     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8485                  *(int *)value_ptr);
8486
8487     // check if any settings have been modified before saving them
8488     if (value != default_value)
8489       modified = TRUE;
8490
8491     // do not save if explicitly told or if unmodified default settings
8492     if ((save_type == SAVE_CONF_NEVER) ||
8493         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8494       return 0;
8495
8496     if (write_element)
8497       num_bytes += putFile16BitBE(file, element);
8498
8499     num_bytes += putFile8Bit(file, conf_type);
8500     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8501                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8502                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8503                   0);
8504   }
8505   else if (data_type == TYPE_STRING)
8506   {
8507     char *default_string = entry->default_string;
8508     char *string = (char *)(entry->value);
8509     int string_length = strlen(string);
8510     int i;
8511
8512     // check if any settings have been modified before saving them
8513     if (!strEqual(string, default_string))
8514       modified = TRUE;
8515
8516     // do not save if explicitly told or if unmodified default settings
8517     if ((save_type == SAVE_CONF_NEVER) ||
8518         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8519       return 0;
8520
8521     if (write_element)
8522       num_bytes += putFile16BitBE(file, element);
8523
8524     num_bytes += putFile8Bit(file, conf_type);
8525     num_bytes += putFile16BitBE(file, string_length);
8526
8527     for (i = 0; i < string_length; i++)
8528       num_bytes += putFile8Bit(file, string[i]);
8529   }
8530   else if (data_type == TYPE_ELEMENT_LIST)
8531   {
8532     int *element_array = (int *)(entry->value);
8533     int num_elements = *(int *)(entry->num_entities);
8534     int i;
8535
8536     // check if any settings have been modified before saving them
8537     for (i = 0; i < num_elements; i++)
8538       if (element_array[i] != default_value)
8539         modified = TRUE;
8540
8541     // do not save if explicitly told or if unmodified default settings
8542     if ((save_type == SAVE_CONF_NEVER) ||
8543         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8544       return 0;
8545
8546     if (write_element)
8547       num_bytes += putFile16BitBE(file, element);
8548
8549     num_bytes += putFile8Bit(file, conf_type);
8550     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8551
8552     for (i = 0; i < num_elements; i++)
8553       num_bytes += putFile16BitBE(file, element_array[i]);
8554   }
8555   else if (data_type == TYPE_CONTENT_LIST)
8556   {
8557     struct Content *content = (struct Content *)(entry->value);
8558     int num_contents = *(int *)(entry->num_entities);
8559     int i, x, y;
8560
8561     // check if any settings have been modified before saving them
8562     for (i = 0; i < num_contents; i++)
8563       for (y = 0; y < 3; y++)
8564         for (x = 0; x < 3; x++)
8565           if (content[i].e[x][y] != 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 += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8578
8579     for (i = 0; i < num_contents; i++)
8580       for (y = 0; y < 3; y++)
8581         for (x = 0; x < 3; x++)
8582           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8583   }
8584
8585   return num_bytes;
8586 }
8587
8588 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8589 {
8590   int chunk_size = 0;
8591   int i;
8592
8593   li = *level;          // copy level data into temporary buffer
8594
8595   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8596     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8597
8598   return chunk_size;
8599 }
8600
8601 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8602 {
8603   int chunk_size = 0;
8604   int i;
8605
8606   li = *level;          // copy level data into temporary buffer
8607
8608   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8609     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8610
8611   return chunk_size;
8612 }
8613
8614 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8615 {
8616   int envelope_nr = element - EL_ENVELOPE_1;
8617   int chunk_size = 0;
8618   int i;
8619
8620   chunk_size += putFile16BitBE(file, element);
8621
8622   // copy envelope data into temporary buffer
8623   xx_envelope = level->envelope[envelope_nr];
8624
8625   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8626     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8627
8628   return chunk_size;
8629 }
8630
8631 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8632 {
8633   struct ElementInfo *ei = &element_info[element];
8634   int chunk_size = 0;
8635   int i, j;
8636
8637   chunk_size += putFile16BitBE(file, element);
8638
8639   xx_ei = *ei;          // copy element data into temporary buffer
8640
8641   // set default description string for this specific element
8642   strcpy(xx_default_description, getDefaultElementDescription(ei));
8643
8644   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8645     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8646
8647   for (i = 0; i < ei->num_change_pages; i++)
8648   {
8649     struct ElementChangeInfo *change = &ei->change_page[i];
8650
8651     xx_current_change_page = i;
8652
8653     xx_change = *change;        // copy change data into temporary buffer
8654
8655     resetEventBits();
8656     setEventBitsFromEventFlags(change);
8657
8658     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8659       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8660                                          FALSE);
8661   }
8662
8663   return chunk_size;
8664 }
8665
8666 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8667 {
8668   struct ElementInfo *ei = &element_info[element];
8669   struct ElementGroupInfo *group = ei->group;
8670   int chunk_size = 0;
8671   int i;
8672
8673   chunk_size += putFile16BitBE(file, element);
8674
8675   xx_ei = *ei;          // copy element data into temporary buffer
8676   xx_group = *group;    // copy group data into temporary buffer
8677
8678   // set default description string for this specific element
8679   strcpy(xx_default_description, getDefaultElementDescription(ei));
8680
8681   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8682     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8683
8684   return chunk_size;
8685 }
8686
8687 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8688 {
8689   struct ElementInfo *ei = &element_info[element];
8690   int chunk_size = 0;
8691   int i;
8692
8693   chunk_size += putFile16BitBE(file, element);
8694
8695   xx_ei = *ei;          // copy element data into temporary buffer
8696
8697   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8698     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8699
8700   return chunk_size;
8701 }
8702
8703 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8704                                   boolean save_as_template)
8705 {
8706   int chunk_size;
8707   int i;
8708   FILE *file;
8709
8710   if (!(file = fopen(filename, MODE_WRITE)))
8711   {
8712     Warn("cannot save level file '%s'", filename);
8713
8714     return;
8715   }
8716
8717   level->file_version = FILE_VERSION_ACTUAL;
8718   level->game_version = GAME_VERSION_ACTUAL;
8719
8720   level->creation_date = getCurrentDate();
8721
8722   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8723   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8724
8725   chunk_size = SaveLevel_VERS(NULL, level);
8726   putFileChunkBE(file, "VERS", chunk_size);
8727   SaveLevel_VERS(file, level);
8728
8729   chunk_size = SaveLevel_DATE(NULL, level);
8730   putFileChunkBE(file, "DATE", chunk_size);
8731   SaveLevel_DATE(file, level);
8732
8733   chunk_size = SaveLevel_NAME(NULL, level);
8734   putFileChunkBE(file, "NAME", chunk_size);
8735   SaveLevel_NAME(file, level);
8736
8737   chunk_size = SaveLevel_AUTH(NULL, level);
8738   putFileChunkBE(file, "AUTH", chunk_size);
8739   SaveLevel_AUTH(file, level);
8740
8741   chunk_size = SaveLevel_INFO(NULL, level);
8742   putFileChunkBE(file, "INFO", chunk_size);
8743   SaveLevel_INFO(file, level);
8744
8745   chunk_size = SaveLevel_BODY(NULL, level);
8746   putFileChunkBE(file, "BODY", chunk_size);
8747   SaveLevel_BODY(file, level);
8748
8749   chunk_size = SaveLevel_ELEM(NULL, level);
8750   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8751   {
8752     putFileChunkBE(file, "ELEM", chunk_size);
8753     SaveLevel_ELEM(file, level);
8754   }
8755
8756   for (i = 0; i < NUM_ENVELOPES; i++)
8757   {
8758     int element = EL_ENVELOPE_1 + i;
8759
8760     chunk_size = SaveLevel_NOTE(NULL, level, element);
8761     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8762     {
8763       putFileChunkBE(file, "NOTE", chunk_size);
8764       SaveLevel_NOTE(file, level, element);
8765     }
8766   }
8767
8768   // if not using template level, check for non-default custom/group elements
8769   if (!level->use_custom_template || save_as_template)
8770   {
8771     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8772     {
8773       int element = EL_CUSTOM_START + i;
8774
8775       chunk_size = SaveLevel_CUSX(NULL, level, element);
8776       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8777       {
8778         putFileChunkBE(file, "CUSX", chunk_size);
8779         SaveLevel_CUSX(file, level, element);
8780       }
8781     }
8782
8783     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8784     {
8785       int element = EL_GROUP_START + i;
8786
8787       chunk_size = SaveLevel_GRPX(NULL, level, element);
8788       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8789       {
8790         putFileChunkBE(file, "GRPX", chunk_size);
8791         SaveLevel_GRPX(file, level, element);
8792       }
8793     }
8794
8795     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8796     {
8797       int element = GET_EMPTY_ELEMENT(i);
8798
8799       chunk_size = SaveLevel_EMPX(NULL, level, element);
8800       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8801       {
8802         putFileChunkBE(file, "EMPX", chunk_size);
8803         SaveLevel_EMPX(file, level, element);
8804       }
8805     }
8806   }
8807
8808   fclose(file);
8809
8810   SetFilePermissions(filename, PERMS_PRIVATE);
8811 }
8812
8813 void SaveLevel(int nr)
8814 {
8815   char *filename = getDefaultLevelFilename(nr);
8816
8817   SaveLevelFromFilename(&level, filename, FALSE);
8818 }
8819
8820 void SaveLevelTemplate(void)
8821 {
8822   char *filename = getLocalLevelTemplateFilename();
8823
8824   SaveLevelFromFilename(&level, filename, TRUE);
8825 }
8826
8827 boolean SaveLevelChecked(int nr)
8828 {
8829   char *filename = getDefaultLevelFilename(nr);
8830   boolean new_level = !fileExists(filename);
8831   boolean level_saved = FALSE;
8832
8833   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8834   {
8835     SaveLevel(nr);
8836
8837     if (new_level)
8838       Request("Level saved!", REQ_CONFIRM);
8839
8840     level_saved = TRUE;
8841   }
8842
8843   return level_saved;
8844 }
8845
8846 void DumpLevel(struct LevelInfo *level)
8847 {
8848   if (level->no_level_file || level->no_valid_file)
8849   {
8850     Warn("cannot dump -- no valid level file found");
8851
8852     return;
8853   }
8854
8855   PrintLine("-", 79);
8856   Print("Level xxx (file version %08d, game version %08d)\n",
8857         level->file_version, level->game_version);
8858   PrintLine("-", 79);
8859
8860   Print("Level author: '%s'\n", level->author);
8861   Print("Level title:  '%s'\n", level->name);
8862   Print("\n");
8863   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8864   Print("\n");
8865   Print("Level time:  %d seconds\n", level->time);
8866   Print("Gems needed: %d\n", level->gems_needed);
8867   Print("\n");
8868   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8869   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8870   Print("Time for light:      %d seconds\n", level->time_light);
8871   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8872   Print("\n");
8873   Print("Amoeba speed: %d\n", level->amoeba_speed);
8874   Print("\n");
8875
8876   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8877   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8878   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8879   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8880   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8881   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8882
8883   if (options.debug)
8884   {
8885     int i, j;
8886
8887     for (i = 0; i < NUM_ENVELOPES; i++)
8888     {
8889       char *text = level->envelope[i].text;
8890       int text_len = strlen(text);
8891       boolean has_text = FALSE;
8892
8893       for (j = 0; j < text_len; j++)
8894         if (text[j] != ' ' && text[j] != '\n')
8895           has_text = TRUE;
8896
8897       if (has_text)
8898       {
8899         Print("\n");
8900         Print("Envelope %d:\n'%s'\n", i + 1, text);
8901       }
8902     }
8903   }
8904
8905   PrintLine("-", 79);
8906 }
8907
8908 void DumpLevels(void)
8909 {
8910   static LevelDirTree *dumplevel_leveldir = NULL;
8911
8912   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8913                                                  global.dumplevel_leveldir);
8914
8915   if (dumplevel_leveldir == NULL)
8916     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8917
8918   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8919       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8920     Fail("no such level number: %d", global.dumplevel_level_nr);
8921
8922   leveldir_current = dumplevel_leveldir;
8923
8924   LoadLevel(global.dumplevel_level_nr);
8925   DumpLevel(&level);
8926
8927   CloseAllAndExit(0);
8928 }
8929
8930
8931 // ============================================================================
8932 // tape file functions
8933 // ============================================================================
8934
8935 static void setTapeInfoToDefaults(void)
8936 {
8937   int i;
8938
8939   // always start with reliable default values (empty tape)
8940   TapeErase();
8941
8942   // default values (also for pre-1.2 tapes) with only the first player
8943   tape.player_participates[0] = TRUE;
8944   for (i = 1; i < MAX_PLAYERS; i++)
8945     tape.player_participates[i] = FALSE;
8946
8947   // at least one (default: the first) player participates in every tape
8948   tape.num_participating_players = 1;
8949
8950   tape.property_bits = TAPE_PROPERTY_NONE;
8951
8952   tape.level_nr = level_nr;
8953   tape.counter = 0;
8954   tape.changed = FALSE;
8955   tape.solved = FALSE;
8956
8957   tape.recording = FALSE;
8958   tape.playing = FALSE;
8959   tape.pausing = FALSE;
8960
8961   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8962   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8963
8964   tape.no_info_chunk = TRUE;
8965   tape.no_valid_file = FALSE;
8966 }
8967
8968 static int getTapePosSize(struct TapeInfo *tape)
8969 {
8970   int tape_pos_size = 0;
8971
8972   if (tape->use_key_actions)
8973     tape_pos_size += tape->num_participating_players;
8974
8975   if (tape->use_mouse_actions)
8976     tape_pos_size += 3;         // x and y position and mouse button mask
8977
8978   tape_pos_size += 1;           // tape action delay value
8979
8980   return tape_pos_size;
8981 }
8982
8983 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8984 {
8985   tape->use_key_actions = FALSE;
8986   tape->use_mouse_actions = FALSE;
8987
8988   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8989     tape->use_key_actions = TRUE;
8990
8991   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8992     tape->use_mouse_actions = TRUE;
8993 }
8994
8995 static int getTapeActionValue(struct TapeInfo *tape)
8996 {
8997   return (tape->use_key_actions &&
8998           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8999           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
9000           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
9001           TAPE_ACTIONS_DEFAULT);
9002 }
9003
9004 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
9005 {
9006   tape->file_version = getFileVersion(file);
9007   tape->game_version = getFileVersion(file);
9008
9009   return chunk_size;
9010 }
9011
9012 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
9013 {
9014   int i;
9015
9016   tape->random_seed = getFile32BitBE(file);
9017   tape->date        = getFile32BitBE(file);
9018   tape->length      = getFile32BitBE(file);
9019
9020   // read header fields that are new since version 1.2
9021   if (tape->file_version >= FILE_VERSION_1_2)
9022   {
9023     byte store_participating_players = getFile8Bit(file);
9024     int engine_version;
9025
9026     // since version 1.2, tapes store which players participate in the tape
9027     tape->num_participating_players = 0;
9028     for (i = 0; i < MAX_PLAYERS; i++)
9029     {
9030       tape->player_participates[i] = FALSE;
9031
9032       if (store_participating_players & (1 << i))
9033       {
9034         tape->player_participates[i] = TRUE;
9035         tape->num_participating_players++;
9036       }
9037     }
9038
9039     setTapeActionFlags(tape, getFile8Bit(file));
9040
9041     tape->property_bits = getFile8Bit(file);
9042     tape->solved = getFile8Bit(file);
9043
9044     engine_version = getFileVersion(file);
9045     if (engine_version > 0)
9046       tape->engine_version = engine_version;
9047     else
9048       tape->engine_version = tape->game_version;
9049   }
9050
9051   return chunk_size;
9052 }
9053
9054 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
9055 {
9056   tape->scr_fieldx = getFile8Bit(file);
9057   tape->scr_fieldy = getFile8Bit(file);
9058
9059   return chunk_size;
9060 }
9061
9062 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
9063 {
9064   char *level_identifier = NULL;
9065   int level_identifier_size;
9066   int i;
9067
9068   tape->no_info_chunk = FALSE;
9069
9070   level_identifier_size = getFile16BitBE(file);
9071
9072   level_identifier = checked_malloc(level_identifier_size);
9073
9074   for (i = 0; i < level_identifier_size; i++)
9075     level_identifier[i] = getFile8Bit(file);
9076
9077   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
9078   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
9079
9080   checked_free(level_identifier);
9081
9082   tape->level_nr = getFile16BitBE(file);
9083
9084   chunk_size = 2 + level_identifier_size + 2;
9085
9086   return chunk_size;
9087 }
9088
9089 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
9090 {
9091   int i, j;
9092   int tape_pos_size = getTapePosSize(tape);
9093   int chunk_size_expected = tape_pos_size * tape->length;
9094
9095   if (chunk_size_expected != chunk_size)
9096   {
9097     ReadUnusedBytesFromFile(file, chunk_size);
9098     return chunk_size_expected;
9099   }
9100
9101   for (i = 0; i < tape->length; i++)
9102   {
9103     if (i >= MAX_TAPE_LEN)
9104     {
9105       Warn("tape truncated -- size exceeds maximum tape size %d",
9106             MAX_TAPE_LEN);
9107
9108       // tape too large; read and ignore remaining tape data from this chunk
9109       for (;i < tape->length; i++)
9110         ReadUnusedBytesFromFile(file, tape_pos_size);
9111
9112       break;
9113     }
9114
9115     if (tape->use_key_actions)
9116     {
9117       for (j = 0; j < MAX_PLAYERS; j++)
9118       {
9119         tape->pos[i].action[j] = MV_NONE;
9120
9121         if (tape->player_participates[j])
9122           tape->pos[i].action[j] = getFile8Bit(file);
9123       }
9124     }
9125
9126     if (tape->use_mouse_actions)
9127     {
9128       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
9129       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
9130       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
9131     }
9132
9133     tape->pos[i].delay = getFile8Bit(file);
9134
9135     if (tape->file_version == FILE_VERSION_1_0)
9136     {
9137       // eliminate possible diagonal moves in old tapes
9138       // this is only for backward compatibility
9139
9140       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
9141       byte action = tape->pos[i].action[0];
9142       int k, num_moves = 0;
9143
9144       for (k = 0; k < 4; k++)
9145       {
9146         if (action & joy_dir[k])
9147         {
9148           tape->pos[i + num_moves].action[0] = joy_dir[k];
9149           if (num_moves > 0)
9150             tape->pos[i + num_moves].delay = 0;
9151           num_moves++;
9152         }
9153       }
9154
9155       if (num_moves > 1)
9156       {
9157         num_moves--;
9158         i += num_moves;
9159         tape->length += num_moves;
9160       }
9161     }
9162     else if (tape->file_version < FILE_VERSION_2_0)
9163     {
9164       // convert pre-2.0 tapes to new tape format
9165
9166       if (tape->pos[i].delay > 1)
9167       {
9168         // action part
9169         tape->pos[i + 1] = tape->pos[i];
9170         tape->pos[i + 1].delay = 1;
9171
9172         // delay part
9173         for (j = 0; j < MAX_PLAYERS; j++)
9174           tape->pos[i].action[j] = MV_NONE;
9175         tape->pos[i].delay--;
9176
9177         i++;
9178         tape->length++;
9179       }
9180     }
9181
9182     if (checkEndOfFile(file))
9183       break;
9184   }
9185
9186   if (i != tape->length)
9187     chunk_size = tape_pos_size * i;
9188
9189   return chunk_size;
9190 }
9191
9192 static void LoadTape_SokobanSolution(char *filename)
9193 {
9194   File *file;
9195   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9196
9197   if (!(file = openFile(filename, MODE_READ)))
9198   {
9199     tape.no_valid_file = TRUE;
9200
9201     return;
9202   }
9203
9204   while (!checkEndOfFile(file))
9205   {
9206     unsigned char c = getByteFromFile(file);
9207
9208     if (checkEndOfFile(file))
9209       break;
9210
9211     switch (c)
9212     {
9213       case 'u':
9214       case 'U':
9215         tape.pos[tape.length].action[0] = MV_UP;
9216         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9217         tape.length++;
9218         break;
9219
9220       case 'd':
9221       case 'D':
9222         tape.pos[tape.length].action[0] = MV_DOWN;
9223         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9224         tape.length++;
9225         break;
9226
9227       case 'l':
9228       case 'L':
9229         tape.pos[tape.length].action[0] = MV_LEFT;
9230         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9231         tape.length++;
9232         break;
9233
9234       case 'r':
9235       case 'R':
9236         tape.pos[tape.length].action[0] = MV_RIGHT;
9237         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9238         tape.length++;
9239         break;
9240
9241       case '\n':
9242       case '\r':
9243       case '\t':
9244       case ' ':
9245         // ignore white-space characters
9246         break;
9247
9248       default:
9249         tape.no_valid_file = TRUE;
9250
9251         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9252
9253         break;
9254     }
9255   }
9256
9257   closeFile(file);
9258
9259   if (tape.no_valid_file)
9260     return;
9261
9262   tape.length_frames  = GetTapeLengthFrames();
9263   tape.length_seconds = GetTapeLengthSeconds();
9264 }
9265
9266 void LoadTapeFromFilename(char *filename)
9267 {
9268   char cookie[MAX_LINE_LEN];
9269   char chunk_name[CHUNK_ID_LEN + 1];
9270   File *file;
9271   int chunk_size;
9272
9273   // always start with reliable default values
9274   setTapeInfoToDefaults();
9275
9276   if (strSuffix(filename, ".sln"))
9277   {
9278     LoadTape_SokobanSolution(filename);
9279
9280     return;
9281   }
9282
9283   if (!(file = openFile(filename, MODE_READ)))
9284   {
9285     tape.no_valid_file = TRUE;
9286
9287     return;
9288   }
9289
9290   getFileChunkBE(file, chunk_name, NULL);
9291   if (strEqual(chunk_name, "RND1"))
9292   {
9293     getFile32BitBE(file);               // not used
9294
9295     getFileChunkBE(file, chunk_name, NULL);
9296     if (!strEqual(chunk_name, "TAPE"))
9297     {
9298       tape.no_valid_file = TRUE;
9299
9300       Warn("unknown format of tape file '%s'", filename);
9301
9302       closeFile(file);
9303
9304       return;
9305     }
9306   }
9307   else  // check for pre-2.0 file format with cookie string
9308   {
9309     strcpy(cookie, chunk_name);
9310     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9311       cookie[4] = '\0';
9312     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9313       cookie[strlen(cookie) - 1] = '\0';
9314
9315     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9316     {
9317       tape.no_valid_file = TRUE;
9318
9319       Warn("unknown format of tape file '%s'", filename);
9320
9321       closeFile(file);
9322
9323       return;
9324     }
9325
9326     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9327     {
9328       tape.no_valid_file = TRUE;
9329
9330       Warn("unsupported version of tape file '%s'", filename);
9331
9332       closeFile(file);
9333
9334       return;
9335     }
9336
9337     // pre-2.0 tape files have no game version, so use file version here
9338     tape.game_version = tape.file_version;
9339   }
9340
9341   if (tape.file_version < FILE_VERSION_1_2)
9342   {
9343     // tape files from versions before 1.2.0 without chunk structure
9344     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9345     LoadTape_BODY(file, 2 * tape.length,      &tape);
9346   }
9347   else
9348   {
9349     static struct
9350     {
9351       char *name;
9352       int size;
9353       int (*loader)(File *, int, struct TapeInfo *);
9354     }
9355     chunk_info[] =
9356     {
9357       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9358       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9359       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9360       { "INFO", -1,                     LoadTape_INFO },
9361       { "BODY", -1,                     LoadTape_BODY },
9362       {  NULL,  0,                      NULL }
9363     };
9364
9365     while (getFileChunkBE(file, chunk_name, &chunk_size))
9366     {
9367       int i = 0;
9368
9369       while (chunk_info[i].name != NULL &&
9370              !strEqual(chunk_name, chunk_info[i].name))
9371         i++;
9372
9373       if (chunk_info[i].name == NULL)
9374       {
9375         Warn("unknown chunk '%s' in tape file '%s'",
9376               chunk_name, filename);
9377
9378         ReadUnusedBytesFromFile(file, chunk_size);
9379       }
9380       else if (chunk_info[i].size != -1 &&
9381                chunk_info[i].size != chunk_size)
9382       {
9383         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9384               chunk_size, chunk_name, filename);
9385
9386         ReadUnusedBytesFromFile(file, chunk_size);
9387       }
9388       else
9389       {
9390         // call function to load this tape chunk
9391         int chunk_size_expected =
9392           (chunk_info[i].loader)(file, chunk_size, &tape);
9393
9394         // the size of some chunks cannot be checked before reading other
9395         // chunks first (like "HEAD" and "BODY") that contain some header
9396         // information, so check them here
9397         if (chunk_size_expected != chunk_size)
9398         {
9399           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9400                 chunk_size, chunk_name, filename);
9401         }
9402       }
9403     }
9404   }
9405
9406   closeFile(file);
9407
9408   tape.length_frames  = GetTapeLengthFrames();
9409   tape.length_seconds = GetTapeLengthSeconds();
9410
9411 #if 0
9412   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9413         tape.file_version);
9414   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9415         tape.game_version);
9416   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9417         tape.engine_version);
9418 #endif
9419 }
9420
9421 void LoadTape(int nr)
9422 {
9423   char *filename = getTapeFilename(nr);
9424
9425   LoadTapeFromFilename(filename);
9426 }
9427
9428 void LoadSolutionTape(int nr)
9429 {
9430   char *filename = getSolutionTapeFilename(nr);
9431
9432   LoadTapeFromFilename(filename);
9433
9434   if (TAPE_IS_EMPTY(tape))
9435   {
9436     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9437         level.native_bd_level->replay != NULL)
9438       CopyNativeTape_BD_to_RND(&level);
9439     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9440         level.native_sp_level->demo.is_available)
9441       CopyNativeTape_SP_to_RND(&level);
9442   }
9443 }
9444
9445 void LoadScoreTape(char *score_tape_basename, int nr)
9446 {
9447   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9448
9449   LoadTapeFromFilename(filename);
9450 }
9451
9452 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9453 {
9454   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9455
9456   LoadTapeFromFilename(filename);
9457 }
9458
9459 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9460 {
9461   // chunk required for team mode tapes with non-default screen size
9462   return (tape->num_participating_players > 1 &&
9463           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9464            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9465 }
9466
9467 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9468 {
9469   putFileVersion(file, tape->file_version);
9470   putFileVersion(file, tape->game_version);
9471 }
9472
9473 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9474 {
9475   int i;
9476   byte store_participating_players = 0;
9477
9478   // set bits for participating players for compact storage
9479   for (i = 0; i < MAX_PLAYERS; i++)
9480     if (tape->player_participates[i])
9481       store_participating_players |= (1 << i);
9482
9483   putFile32BitBE(file, tape->random_seed);
9484   putFile32BitBE(file, tape->date);
9485   putFile32BitBE(file, tape->length);
9486
9487   putFile8Bit(file, store_participating_players);
9488
9489   putFile8Bit(file, getTapeActionValue(tape));
9490
9491   putFile8Bit(file, tape->property_bits);
9492   putFile8Bit(file, tape->solved);
9493
9494   putFileVersion(file, tape->engine_version);
9495 }
9496
9497 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9498 {
9499   putFile8Bit(file, tape->scr_fieldx);
9500   putFile8Bit(file, tape->scr_fieldy);
9501 }
9502
9503 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9504 {
9505   int level_identifier_size = strlen(tape->level_identifier) + 1;
9506   int i;
9507
9508   putFile16BitBE(file, level_identifier_size);
9509
9510   for (i = 0; i < level_identifier_size; i++)
9511     putFile8Bit(file, tape->level_identifier[i]);
9512
9513   putFile16BitBE(file, tape->level_nr);
9514 }
9515
9516 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9517 {
9518   int i, j;
9519
9520   for (i = 0; i < tape->length; i++)
9521   {
9522     if (tape->use_key_actions)
9523     {
9524       for (j = 0; j < MAX_PLAYERS; j++)
9525         if (tape->player_participates[j])
9526           putFile8Bit(file, tape->pos[i].action[j]);
9527     }
9528
9529     if (tape->use_mouse_actions)
9530     {
9531       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9532       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9533       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9534     }
9535
9536     putFile8Bit(file, tape->pos[i].delay);
9537   }
9538 }
9539
9540 void SaveTapeToFilename(char *filename)
9541 {
9542   FILE *file;
9543   int tape_pos_size;
9544   int info_chunk_size;
9545   int body_chunk_size;
9546
9547   if (!(file = fopen(filename, MODE_WRITE)))
9548   {
9549     Warn("cannot save level recording file '%s'", filename);
9550
9551     return;
9552   }
9553
9554   tape_pos_size = getTapePosSize(&tape);
9555
9556   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9557   body_chunk_size = tape_pos_size * tape.length;
9558
9559   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9560   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9561
9562   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9563   SaveTape_VERS(file, &tape);
9564
9565   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9566   SaveTape_HEAD(file, &tape);
9567
9568   if (checkSaveTape_SCRN(&tape))
9569   {
9570     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9571     SaveTape_SCRN(file, &tape);
9572   }
9573
9574   putFileChunkBE(file, "INFO", info_chunk_size);
9575   SaveTape_INFO(file, &tape);
9576
9577   putFileChunkBE(file, "BODY", body_chunk_size);
9578   SaveTape_BODY(file, &tape);
9579
9580   fclose(file);
9581
9582   SetFilePermissions(filename, PERMS_PRIVATE);
9583 }
9584
9585 static void SaveTapeExt(char *filename)
9586 {
9587   int i;
9588
9589   tape.file_version = FILE_VERSION_ACTUAL;
9590   tape.game_version = GAME_VERSION_ACTUAL;
9591
9592   tape.num_participating_players = 0;
9593
9594   // count number of participating players
9595   for (i = 0; i < MAX_PLAYERS; i++)
9596     if (tape.player_participates[i])
9597       tape.num_participating_players++;
9598
9599   SaveTapeToFilename(filename);
9600
9601   tape.changed = FALSE;
9602 }
9603
9604 void SaveTape(int nr)
9605 {
9606   char *filename = getTapeFilename(nr);
9607
9608   InitTapeDirectory(leveldir_current->subdir);
9609
9610   SaveTapeExt(filename);
9611 }
9612
9613 void SaveScoreTape(int nr)
9614 {
9615   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9616
9617   // used instead of "leveldir_current->subdir" (for network games)
9618   InitScoreTapeDirectory(levelset.identifier, nr);
9619
9620   SaveTapeExt(filename);
9621 }
9622
9623 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9624                                   unsigned int req_state_added)
9625 {
9626   char *filename = getTapeFilename(nr);
9627   boolean new_tape = !fileExists(filename);
9628   boolean tape_saved = FALSE;
9629
9630   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9631   {
9632     SaveTape(nr);
9633
9634     if (new_tape)
9635       Request(msg_saved, REQ_CONFIRM | req_state_added);
9636
9637     tape_saved = TRUE;
9638   }
9639
9640   return tape_saved;
9641 }
9642
9643 boolean SaveTapeChecked(int nr)
9644 {
9645   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9646 }
9647
9648 boolean SaveTapeChecked_LevelSolved(int nr)
9649 {
9650   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9651                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9652 }
9653
9654 void DumpTape(struct TapeInfo *tape)
9655 {
9656   int tape_frame_counter;
9657   int i, j;
9658
9659   if (tape->no_valid_file)
9660   {
9661     Warn("cannot dump -- no valid tape file found");
9662
9663     return;
9664   }
9665
9666   PrintLine("-", 79);
9667
9668   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9669         tape->level_nr, tape->file_version, tape->game_version);
9670   Print("                  (effective engine version %08d)\n",
9671         tape->engine_version);
9672   Print("Level series identifier: '%s'\n", tape->level_identifier);
9673
9674   Print("Solution tape: %s\n",
9675         tape->solved ? "yes" :
9676         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9677
9678   Print("Special tape properties: ");
9679   if (tape->property_bits == TAPE_PROPERTY_NONE)
9680     Print("[none]");
9681   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9682     Print("[em_random_bug]");
9683   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9684     Print("[game_speed]");
9685   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9686     Print("[pause]");
9687   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9688     Print("[single_step]");
9689   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9690     Print("[snapshot]");
9691   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9692     Print("[replayed]");
9693   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9694     Print("[tas_keys]");
9695   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9696     Print("[small_graphics]");
9697   Print("\n");
9698
9699   int year2 = tape->date / 10000;
9700   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9701   int month_index_raw = (tape->date / 100) % 100;
9702   int month_index = month_index_raw % 12;       // prevent invalid index
9703   int month = month_index + 1;
9704   int day = tape->date % 100;
9705
9706   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9707
9708   PrintLine("-", 79);
9709
9710   tape_frame_counter = 0;
9711
9712   for (i = 0; i < tape->length; i++)
9713   {
9714     if (i >= MAX_TAPE_LEN)
9715       break;
9716
9717     Print("%04d: ", i);
9718
9719     for (j = 0; j < MAX_PLAYERS; j++)
9720     {
9721       if (tape->player_participates[j])
9722       {
9723         int action = tape->pos[i].action[j];
9724
9725         Print("%d:%02x ", j, action);
9726         Print("[%c%c%c%c|%c%c] - ",
9727               (action & JOY_LEFT ? '<' : ' '),
9728               (action & JOY_RIGHT ? '>' : ' '),
9729               (action & JOY_UP ? '^' : ' '),
9730               (action & JOY_DOWN ? 'v' : ' '),
9731               (action & JOY_BUTTON_1 ? '1' : ' '),
9732               (action & JOY_BUTTON_2 ? '2' : ' '));
9733       }
9734     }
9735
9736     Print("(%03d) ", tape->pos[i].delay);
9737     Print("[%05d]\n", tape_frame_counter);
9738
9739     tape_frame_counter += tape->pos[i].delay;
9740   }
9741
9742   PrintLine("-", 79);
9743 }
9744
9745 void DumpTapes(void)
9746 {
9747   static LevelDirTree *dumptape_leveldir = NULL;
9748
9749   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9750                                                 global.dumptape_leveldir);
9751
9752   if (dumptape_leveldir == NULL)
9753     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9754
9755   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9756       global.dumptape_level_nr > dumptape_leveldir->last_level)
9757     Fail("no such level number: %d", global.dumptape_level_nr);
9758
9759   leveldir_current = dumptape_leveldir;
9760
9761   if (options.mytapes)
9762     LoadTape(global.dumptape_level_nr);
9763   else
9764     LoadSolutionTape(global.dumptape_level_nr);
9765
9766   DumpTape(&tape);
9767
9768   CloseAllAndExit(0);
9769 }
9770
9771
9772 // ============================================================================
9773 // score file functions
9774 // ============================================================================
9775
9776 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9777 {
9778   int i;
9779
9780   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9781   {
9782     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9783     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9784     scores->entry[i].score = 0;
9785     scores->entry[i].time = 0;
9786
9787     scores->entry[i].id = -1;
9788     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9789     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9790     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9791     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9792     strcpy(scores->entry[i].country_code, "??");
9793   }
9794
9795   scores->num_entries = 0;
9796   scores->last_added = -1;
9797   scores->last_added_local = -1;
9798
9799   scores->updated = FALSE;
9800   scores->uploaded = FALSE;
9801   scores->tape_downloaded = FALSE;
9802   scores->force_last_added = FALSE;
9803
9804   // The following values are intentionally not reset here:
9805   // - last_level_nr
9806   // - last_entry_nr
9807   // - next_level_nr
9808   // - continue_playing
9809   // - continue_on_return
9810 }
9811
9812 static void setScoreInfoToDefaults(void)
9813 {
9814   setScoreInfoToDefaultsExt(&scores);
9815 }
9816
9817 static void setServerScoreInfoToDefaults(void)
9818 {
9819   setScoreInfoToDefaultsExt(&server_scores);
9820 }
9821
9822 static void LoadScore_OLD(int nr)
9823 {
9824   int i;
9825   char *filename = getScoreFilename(nr);
9826   char cookie[MAX_LINE_LEN];
9827   char line[MAX_LINE_LEN];
9828   char *line_ptr;
9829   FILE *file;
9830
9831   if (!(file = fopen(filename, MODE_READ)))
9832     return;
9833
9834   // check file identifier
9835   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9836     cookie[0] = '\0';
9837   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9838     cookie[strlen(cookie) - 1] = '\0';
9839
9840   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9841   {
9842     Warn("unknown format of score file '%s'", filename);
9843
9844     fclose(file);
9845
9846     return;
9847   }
9848
9849   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9850   {
9851     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9852       Warn("fscanf() failed; %s", strerror(errno));
9853
9854     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9855       line[0] = '\0';
9856
9857     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9858       line[strlen(line) - 1] = '\0';
9859
9860     for (line_ptr = line; *line_ptr; line_ptr++)
9861     {
9862       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9863       {
9864         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9865         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9866         break;
9867       }
9868     }
9869   }
9870
9871   fclose(file);
9872 }
9873
9874 static void ConvertScore_OLD(void)
9875 {
9876   // only convert score to time for levels that rate playing time over score
9877   if (!level.rate_time_over_score)
9878     return;
9879
9880   // convert old score to playing time for score-less levels (like Supaplex)
9881   int time_final_max = 999;
9882   int i;
9883
9884   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9885   {
9886     int score = scores.entry[i].score;
9887
9888     if (score > 0 && score < time_final_max)
9889       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9890   }
9891 }
9892
9893 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9894 {
9895   scores->file_version = getFileVersion(file);
9896   scores->game_version = getFileVersion(file);
9897
9898   return chunk_size;
9899 }
9900
9901 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9902 {
9903   char *level_identifier = NULL;
9904   int level_identifier_size;
9905   int i;
9906
9907   level_identifier_size = getFile16BitBE(file);
9908
9909   level_identifier = checked_malloc(level_identifier_size);
9910
9911   for (i = 0; i < level_identifier_size; i++)
9912     level_identifier[i] = getFile8Bit(file);
9913
9914   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9915   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9916
9917   checked_free(level_identifier);
9918
9919   scores->level_nr = getFile16BitBE(file);
9920   scores->num_entries = getFile16BitBE(file);
9921
9922   chunk_size = 2 + level_identifier_size + 2 + 2;
9923
9924   return chunk_size;
9925 }
9926
9927 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9928 {
9929   int i, j;
9930
9931   for (i = 0; i < scores->num_entries; i++)
9932   {
9933     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9934       scores->entry[i].name[j] = getFile8Bit(file);
9935
9936     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9937   }
9938
9939   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9940
9941   return chunk_size;
9942 }
9943
9944 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9945 {
9946   int i;
9947
9948   for (i = 0; i < scores->num_entries; i++)
9949     scores->entry[i].score = getFile16BitBE(file);
9950
9951   chunk_size = scores->num_entries * 2;
9952
9953   return chunk_size;
9954 }
9955
9956 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9957 {
9958   int i;
9959
9960   for (i = 0; i < scores->num_entries; i++)
9961     scores->entry[i].score = getFile32BitBE(file);
9962
9963   chunk_size = scores->num_entries * 4;
9964
9965   return chunk_size;
9966 }
9967
9968 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9969 {
9970   int i;
9971
9972   for (i = 0; i < scores->num_entries; i++)
9973     scores->entry[i].time = getFile32BitBE(file);
9974
9975   chunk_size = scores->num_entries * 4;
9976
9977   return chunk_size;
9978 }
9979
9980 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9981 {
9982   int i, j;
9983
9984   for (i = 0; i < scores->num_entries; i++)
9985   {
9986     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9987       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9988
9989     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9990   }
9991
9992   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9993
9994   return chunk_size;
9995 }
9996
9997 void LoadScore(int nr)
9998 {
9999   char *filename = getScoreFilename(nr);
10000   char cookie[MAX_LINE_LEN];
10001   char chunk_name[CHUNK_ID_LEN + 1];
10002   int chunk_size;
10003   boolean old_score_file_format = FALSE;
10004   File *file;
10005
10006   // always start with reliable default values
10007   setScoreInfoToDefaults();
10008
10009   if (!(file = openFile(filename, MODE_READ)))
10010     return;
10011
10012   getFileChunkBE(file, chunk_name, NULL);
10013   if (strEqual(chunk_name, "RND1"))
10014   {
10015     getFile32BitBE(file);               // not used
10016
10017     getFileChunkBE(file, chunk_name, NULL);
10018     if (!strEqual(chunk_name, "SCOR"))
10019     {
10020       Warn("unknown format of score file '%s'", filename);
10021
10022       closeFile(file);
10023
10024       return;
10025     }
10026   }
10027   else  // check for old file format with cookie string
10028   {
10029     strcpy(cookie, chunk_name);
10030     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
10031       cookie[4] = '\0';
10032     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
10033       cookie[strlen(cookie) - 1] = '\0';
10034
10035     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
10036     {
10037       Warn("unknown format of score file '%s'", filename);
10038
10039       closeFile(file);
10040
10041       return;
10042     }
10043
10044     old_score_file_format = TRUE;
10045   }
10046
10047   if (old_score_file_format)
10048   {
10049     // score files from versions before 4.2.4.0 without chunk structure
10050     LoadScore_OLD(nr);
10051
10052     // convert score to time, if possible (mainly for Supaplex levels)
10053     ConvertScore_OLD();
10054   }
10055   else
10056   {
10057     static struct
10058     {
10059       char *name;
10060       int size;
10061       int (*loader)(File *, int, struct ScoreInfo *);
10062     }
10063     chunk_info[] =
10064     {
10065       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
10066       { "INFO", -1,                     LoadScore_INFO },
10067       { "NAME", -1,                     LoadScore_NAME },
10068       { "SCOR", -1,                     LoadScore_SCOR },
10069       { "SC4R", -1,                     LoadScore_SC4R },
10070       { "TIME", -1,                     LoadScore_TIME },
10071       { "TAPE", -1,                     LoadScore_TAPE },
10072
10073       {  NULL,  0,                      NULL }
10074     };
10075
10076     while (getFileChunkBE(file, chunk_name, &chunk_size))
10077     {
10078       int i = 0;
10079
10080       while (chunk_info[i].name != NULL &&
10081              !strEqual(chunk_name, chunk_info[i].name))
10082         i++;
10083
10084       if (chunk_info[i].name == NULL)
10085       {
10086         Warn("unknown chunk '%s' in score file '%s'",
10087               chunk_name, filename);
10088
10089         ReadUnusedBytesFromFile(file, chunk_size);
10090       }
10091       else if (chunk_info[i].size != -1 &&
10092                chunk_info[i].size != chunk_size)
10093       {
10094         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10095               chunk_size, chunk_name, filename);
10096
10097         ReadUnusedBytesFromFile(file, chunk_size);
10098       }
10099       else
10100       {
10101         // call function to load this score chunk
10102         int chunk_size_expected =
10103           (chunk_info[i].loader)(file, chunk_size, &scores);
10104
10105         // the size of some chunks cannot be checked before reading other
10106         // chunks first (like "HEAD" and "BODY") that contain some header
10107         // information, so check them here
10108         if (chunk_size_expected != chunk_size)
10109         {
10110           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
10111                 chunk_size, chunk_name, filename);
10112         }
10113       }
10114     }
10115   }
10116
10117   closeFile(file);
10118 }
10119
10120 #if ENABLE_HISTORIC_CHUNKS
10121 void SaveScore_OLD(int nr)
10122 {
10123   int i;
10124   char *filename = getScoreFilename(nr);
10125   FILE *file;
10126
10127   // used instead of "leveldir_current->subdir" (for network games)
10128   InitScoreDirectory(levelset.identifier);
10129
10130   if (!(file = fopen(filename, MODE_WRITE)))
10131   {
10132     Warn("cannot save score for level %d", nr);
10133
10134     return;
10135   }
10136
10137   fprintf(file, "%s\n\n", SCORE_COOKIE);
10138
10139   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10140     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
10141
10142   fclose(file);
10143
10144   SetFilePermissions(filename, PERMS_PRIVATE);
10145 }
10146 #endif
10147
10148 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
10149 {
10150   putFileVersion(file, scores->file_version);
10151   putFileVersion(file, scores->game_version);
10152 }
10153
10154 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10155 {
10156   int level_identifier_size = strlen(scores->level_identifier) + 1;
10157   int i;
10158
10159   putFile16BitBE(file, level_identifier_size);
10160
10161   for (i = 0; i < level_identifier_size; i++)
10162     putFile8Bit(file, scores->level_identifier[i]);
10163
10164   putFile16BitBE(file, scores->level_nr);
10165   putFile16BitBE(file, scores->num_entries);
10166 }
10167
10168 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10169 {
10170   int i, j;
10171
10172   for (i = 0; i < scores->num_entries; i++)
10173   {
10174     int name_size = strlen(scores->entry[i].name);
10175
10176     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10177       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10178   }
10179 }
10180
10181 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10182 {
10183   int i;
10184
10185   for (i = 0; i < scores->num_entries; i++)
10186     putFile16BitBE(file, scores->entry[i].score);
10187 }
10188
10189 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10190 {
10191   int i;
10192
10193   for (i = 0; i < scores->num_entries; i++)
10194     putFile32BitBE(file, scores->entry[i].score);
10195 }
10196
10197 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10198 {
10199   int i;
10200
10201   for (i = 0; i < scores->num_entries; i++)
10202     putFile32BitBE(file, scores->entry[i].time);
10203 }
10204
10205 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10206 {
10207   int i, j;
10208
10209   for (i = 0; i < scores->num_entries; i++)
10210   {
10211     int size = strlen(scores->entry[i].tape_basename);
10212
10213     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10214       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10215   }
10216 }
10217
10218 static void SaveScoreToFilename(char *filename)
10219 {
10220   FILE *file;
10221   int info_chunk_size;
10222   int name_chunk_size;
10223   int scor_chunk_size;
10224   int sc4r_chunk_size;
10225   int time_chunk_size;
10226   int tape_chunk_size;
10227   boolean has_large_score_values;
10228   int i;
10229
10230   if (!(file = fopen(filename, MODE_WRITE)))
10231   {
10232     Warn("cannot save score file '%s'", filename);
10233
10234     return;
10235   }
10236
10237   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10238   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10239   scor_chunk_size = scores.num_entries * 2;
10240   sc4r_chunk_size = scores.num_entries * 4;
10241   time_chunk_size = scores.num_entries * 4;
10242   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10243
10244   has_large_score_values = FALSE;
10245   for (i = 0; i < scores.num_entries; i++)
10246     if (scores.entry[i].score > 0xffff)
10247       has_large_score_values = TRUE;
10248
10249   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10250   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10251
10252   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10253   SaveScore_VERS(file, &scores);
10254
10255   putFileChunkBE(file, "INFO", info_chunk_size);
10256   SaveScore_INFO(file, &scores);
10257
10258   putFileChunkBE(file, "NAME", name_chunk_size);
10259   SaveScore_NAME(file, &scores);
10260
10261   if (has_large_score_values)
10262   {
10263     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10264     SaveScore_SC4R(file, &scores);
10265   }
10266   else
10267   {
10268     putFileChunkBE(file, "SCOR", scor_chunk_size);
10269     SaveScore_SCOR(file, &scores);
10270   }
10271
10272   putFileChunkBE(file, "TIME", time_chunk_size);
10273   SaveScore_TIME(file, &scores);
10274
10275   putFileChunkBE(file, "TAPE", tape_chunk_size);
10276   SaveScore_TAPE(file, &scores);
10277
10278   fclose(file);
10279
10280   SetFilePermissions(filename, PERMS_PRIVATE);
10281 }
10282
10283 void SaveScore(int nr)
10284 {
10285   char *filename = getScoreFilename(nr);
10286   int i;
10287
10288   // used instead of "leveldir_current->subdir" (for network games)
10289   InitScoreDirectory(levelset.identifier);
10290
10291   scores.file_version = FILE_VERSION_ACTUAL;
10292   scores.game_version = GAME_VERSION_ACTUAL;
10293
10294   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10295   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10296   scores.level_nr = level_nr;
10297
10298   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10299     if (scores.entry[i].score == 0 &&
10300         scores.entry[i].time == 0 &&
10301         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10302       break;
10303
10304   scores.num_entries = i;
10305
10306   if (scores.num_entries == 0)
10307     return;
10308
10309   SaveScoreToFilename(filename);
10310 }
10311
10312 static void LoadServerScoreFromCache(int nr)
10313 {
10314   struct ScoreEntry score_entry;
10315   struct
10316   {
10317     void *value;
10318     boolean is_string;
10319     int string_size;
10320   }
10321   score_mapping[] =
10322   {
10323     { &score_entry.score,               FALSE,  0                       },
10324     { &score_entry.time,                FALSE,  0                       },
10325     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10326     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10327     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10328     { &score_entry.id,                  FALSE,  0                       },
10329     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10330     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10331     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10332     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10333
10334     { NULL,                             FALSE,  0                       }
10335   };
10336   char *filename = getScoreCacheFilename(nr);
10337   SetupFileHash *score_hash = loadSetupFileHash(filename);
10338   int i, j;
10339
10340   server_scores.num_entries = 0;
10341
10342   if (score_hash == NULL)
10343     return;
10344
10345   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10346   {
10347     score_entry = server_scores.entry[i];
10348
10349     for (j = 0; score_mapping[j].value != NULL; j++)
10350     {
10351       char token[10];
10352
10353       sprintf(token, "%02d.%d", i, j);
10354
10355       char *value = getHashEntry(score_hash, token);
10356
10357       if (value == NULL)
10358         continue;
10359
10360       if (score_mapping[j].is_string)
10361       {
10362         char *score_value = (char *)score_mapping[j].value;
10363         int value_size = score_mapping[j].string_size;
10364
10365         strncpy(score_value, value, value_size);
10366         score_value[value_size] = '\0';
10367       }
10368       else
10369       {
10370         int *score_value = (int *)score_mapping[j].value;
10371
10372         *score_value = atoi(value);
10373       }
10374
10375       server_scores.num_entries = i + 1;
10376     }
10377
10378     server_scores.entry[i] = score_entry;
10379   }
10380
10381   freeSetupFileHash(score_hash);
10382 }
10383
10384 void LoadServerScore(int nr, boolean download_score)
10385 {
10386   if (!setup.use_api_server)
10387     return;
10388
10389   // always start with reliable default values
10390   setServerScoreInfoToDefaults();
10391
10392   // 1st step: load server scores from cache file (which may not exist)
10393   // (this should prevent reading it while the thread is writing to it)
10394   LoadServerScoreFromCache(nr);
10395
10396   if (download_score && runtime.use_api_server)
10397   {
10398     // 2nd step: download server scores from score server to cache file
10399     // (as thread, as it might time out if the server is not reachable)
10400     ApiGetScoreAsThread(nr);
10401   }
10402 }
10403
10404 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10405 {
10406   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10407
10408   // if score tape not uploaded, ask for uploading missing tapes later
10409   if (!setup.has_remaining_tapes)
10410     setup.ask_for_remaining_tapes = TRUE;
10411
10412   setup.provide_uploading_tapes = TRUE;
10413   setup.has_remaining_tapes = TRUE;
10414
10415   SaveSetup_ServerSetup();
10416 }
10417
10418 void SaveServerScore(int nr, boolean tape_saved)
10419 {
10420   if (!runtime.use_api_server)
10421   {
10422     PrepareScoreTapesForUpload(leveldir_current->subdir);
10423
10424     return;
10425   }
10426
10427   ApiAddScoreAsThread(nr, tape_saved, NULL);
10428 }
10429
10430 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10431                              char *score_tape_filename)
10432 {
10433   if (!runtime.use_api_server)
10434     return;
10435
10436   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10437 }
10438
10439 void LoadLocalAndServerScore(int nr, boolean download_score)
10440 {
10441   int last_added_local = scores.last_added_local;
10442   boolean force_last_added = scores.force_last_added;
10443
10444   // needed if only showing server scores
10445   setScoreInfoToDefaults();
10446
10447   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10448     LoadScore(nr);
10449
10450   // restore last added local score entry (before merging server scores)
10451   scores.last_added = scores.last_added_local = last_added_local;
10452
10453   if (setup.use_api_server &&
10454       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10455   {
10456     // load server scores from cache file and trigger update from server
10457     LoadServerScore(nr, download_score);
10458
10459     // merge local scores with scores from server
10460     MergeServerScore();
10461   }
10462
10463   if (force_last_added)
10464     scores.force_last_added = force_last_added;
10465 }
10466
10467
10468 // ============================================================================
10469 // setup file functions
10470 // ============================================================================
10471
10472 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10473
10474
10475 static struct TokenInfo global_setup_tokens[] =
10476 {
10477   {
10478     TYPE_STRING,
10479     &setup.player_name,                         "player_name"
10480   },
10481   {
10482     TYPE_SWITCH,
10483     &setup.multiple_users,                      "multiple_users"
10484   },
10485   {
10486     TYPE_SWITCH,
10487     &setup.sound,                               "sound"
10488   },
10489   {
10490     TYPE_SWITCH,
10491     &setup.sound_loops,                         "repeating_sound_loops"
10492   },
10493   {
10494     TYPE_SWITCH,
10495     &setup.sound_music,                         "background_music"
10496   },
10497   {
10498     TYPE_SWITCH,
10499     &setup.sound_simple,                        "simple_sound_effects"
10500   },
10501   {
10502     TYPE_SWITCH,
10503     &setup.toons,                               "toons"
10504   },
10505   {
10506     TYPE_SWITCH,
10507     &setup.global_animations,                   "global_animations"
10508   },
10509   {
10510     TYPE_SWITCH,
10511     &setup.scroll_delay,                        "scroll_delay"
10512   },
10513   {
10514     TYPE_SWITCH,
10515     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10516   },
10517   {
10518     TYPE_INTEGER,
10519     &setup.scroll_delay_value,                  "scroll_delay_value"
10520   },
10521   {
10522     TYPE_STRING,
10523     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10524   },
10525   {
10526     TYPE_INTEGER,
10527     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10528   },
10529   {
10530     TYPE_SWITCH,
10531     &setup.fade_screens,                        "fade_screens"
10532   },
10533   {
10534     TYPE_SWITCH,
10535     &setup.autorecord,                          "automatic_tape_recording"
10536   },
10537   {
10538     TYPE_SWITCH,
10539     &setup.autorecord_after_replay,             "autorecord_after_replay"
10540   },
10541   {
10542     TYPE_SWITCH,
10543     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10544   },
10545   {
10546     TYPE_SWITCH,
10547     &setup.show_titlescreen,                    "show_titlescreen"
10548   },
10549   {
10550     TYPE_SWITCH,
10551     &setup.quick_doors,                         "quick_doors"
10552   },
10553   {
10554     TYPE_SWITCH,
10555     &setup.team_mode,                           "team_mode"
10556   },
10557   {
10558     TYPE_SWITCH,
10559     &setup.handicap,                            "handicap"
10560   },
10561   {
10562     TYPE_SWITCH,
10563     &setup.skip_levels,                         "skip_levels"
10564   },
10565   {
10566     TYPE_SWITCH,
10567     &setup.increment_levels,                    "increment_levels"
10568   },
10569   {
10570     TYPE_SWITCH,
10571     &setup.auto_play_next_level,                "auto_play_next_level"
10572   },
10573   {
10574     TYPE_SWITCH,
10575     &setup.count_score_after_game,              "count_score_after_game"
10576   },
10577   {
10578     TYPE_SWITCH,
10579     &setup.show_scores_after_game,              "show_scores_after_game"
10580   },
10581   {
10582     TYPE_SWITCH,
10583     &setup.time_limit,                          "time_limit"
10584   },
10585   {
10586     TYPE_SWITCH,
10587     &setup.fullscreen,                          "fullscreen"
10588   },
10589   {
10590     TYPE_INTEGER,
10591     &setup.window_scaling_percent,              "window_scaling_percent"
10592   },
10593   {
10594     TYPE_STRING,
10595     &setup.window_scaling_quality,              "window_scaling_quality"
10596   },
10597   {
10598     TYPE_STRING,
10599     &setup.screen_rendering_mode,               "screen_rendering_mode"
10600   },
10601   {
10602     TYPE_STRING,
10603     &setup.vsync_mode,                          "vsync_mode"
10604   },
10605   {
10606     TYPE_SWITCH,
10607     &setup.ask_on_escape,                       "ask_on_escape"
10608   },
10609   {
10610     TYPE_SWITCH,
10611     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10612   },
10613   {
10614     TYPE_SWITCH,
10615     &setup.ask_on_game_over,                    "ask_on_game_over"
10616   },
10617   {
10618     TYPE_SWITCH,
10619     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10620   },
10621   {
10622     TYPE_SWITCH,
10623     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10624   },
10625   {
10626     TYPE_SWITCH,
10627     &setup.quick_switch,                        "quick_player_switch"
10628   },
10629   {
10630     TYPE_SWITCH,
10631     &setup.input_on_focus,                      "input_on_focus"
10632   },
10633   {
10634     TYPE_SWITCH,
10635     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10636   },
10637   {
10638     TYPE_SWITCH,
10639     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10640   },
10641   {
10642     TYPE_SWITCH,
10643     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10644   },
10645   {
10646     TYPE_SWITCH,
10647     &setup.game_speed_extended,                 "game_speed_extended"
10648   },
10649   {
10650     TYPE_INTEGER,
10651     &setup.game_frame_delay,                    "game_frame_delay"
10652   },
10653   {
10654     TYPE_SWITCH,
10655     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10656   },
10657   {
10658     TYPE_SWITCH,
10659     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10660   },
10661   {
10662     TYPE_SWITCH,
10663     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10664   },
10665   {
10666     TYPE_SWITCH3,
10667     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10668   },
10669   {
10670     TYPE_SWITCH,
10671     &setup.sp_show_border_elements,             "sp_show_border_elements"
10672   },
10673   {
10674     TYPE_SWITCH,
10675     &setup.small_game_graphics,                 "small_game_graphics"
10676   },
10677   {
10678     TYPE_SWITCH,
10679     &setup.show_load_save_buttons,              "show_load_save_buttons"
10680   },
10681   {
10682     TYPE_SWITCH,
10683     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10684   },
10685   {
10686     TYPE_STRING,
10687     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10688   },
10689   {
10690     TYPE_STRING,
10691     &setup.graphics_set,                        "graphics_set"
10692   },
10693   {
10694     TYPE_STRING,
10695     &setup.sounds_set,                          "sounds_set"
10696   },
10697   {
10698     TYPE_STRING,
10699     &setup.music_set,                           "music_set"
10700   },
10701   {
10702     TYPE_SWITCH3,
10703     &setup.override_level_graphics,             "override_level_graphics"
10704   },
10705   {
10706     TYPE_SWITCH3,
10707     &setup.override_level_sounds,               "override_level_sounds"
10708   },
10709   {
10710     TYPE_SWITCH3,
10711     &setup.override_level_music,                "override_level_music"
10712   },
10713   {
10714     TYPE_INTEGER,
10715     &setup.volume_simple,                       "volume_simple"
10716   },
10717   {
10718     TYPE_INTEGER,
10719     &setup.volume_loops,                        "volume_loops"
10720   },
10721   {
10722     TYPE_INTEGER,
10723     &setup.volume_music,                        "volume_music"
10724   },
10725   {
10726     TYPE_SWITCH,
10727     &setup.network_mode,                        "network_mode"
10728   },
10729   {
10730     TYPE_PLAYER,
10731     &setup.network_player_nr,                   "network_player"
10732   },
10733   {
10734     TYPE_STRING,
10735     &setup.network_server_hostname,             "network_server_hostname"
10736   },
10737   {
10738     TYPE_STRING,
10739     &setup.touch.control_type,                  "touch.control_type"
10740   },
10741   {
10742     TYPE_INTEGER,
10743     &setup.touch.move_distance,                 "touch.move_distance"
10744   },
10745   {
10746     TYPE_INTEGER,
10747     &setup.touch.drop_distance,                 "touch.drop_distance"
10748   },
10749   {
10750     TYPE_INTEGER,
10751     &setup.touch.transparency,                  "touch.transparency"
10752   },
10753   {
10754     TYPE_INTEGER,
10755     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10756   },
10757   {
10758     TYPE_INTEGER,
10759     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10760   },
10761   {
10762     TYPE_INTEGER,
10763     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10764   },
10765   {
10766     TYPE_INTEGER,
10767     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10768   },
10769   {
10770     TYPE_INTEGER,
10771     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10772   },
10773   {
10774     TYPE_INTEGER,
10775     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10776   },
10777   {
10778     TYPE_SWITCH,
10779     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10780   },
10781 };
10782
10783 static struct TokenInfo auto_setup_tokens[] =
10784 {
10785   {
10786     TYPE_INTEGER,
10787     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10788   },
10789 };
10790
10791 static struct TokenInfo server_setup_tokens[] =
10792 {
10793   {
10794     TYPE_STRING,
10795     &setup.player_uuid,                         "player_uuid"
10796   },
10797   {
10798     TYPE_INTEGER,
10799     &setup.player_version,                      "player_version"
10800   },
10801   {
10802     TYPE_SWITCH,
10803     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10804   },
10805   {
10806     TYPE_STRING,
10807     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10808   },
10809   {
10810     TYPE_STRING,
10811     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10812   },
10813   {
10814     TYPE_SWITCH,
10815     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10816   },
10817   {
10818     TYPE_SWITCH,
10819     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10820   },
10821   {
10822     TYPE_SWITCH,
10823     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10824   },
10825   {
10826     TYPE_SWITCH,
10827     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10828   },
10829   {
10830     TYPE_SWITCH,
10831     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10832   },
10833 };
10834
10835 static struct TokenInfo editor_setup_tokens[] =
10836 {
10837   {
10838     TYPE_SWITCH,
10839     &setup.editor.el_classic,                   "editor.el_classic"
10840   },
10841   {
10842     TYPE_SWITCH,
10843     &setup.editor.el_custom,                    "editor.el_custom"
10844   },
10845   {
10846     TYPE_SWITCH,
10847     &setup.editor.el_user_defined,              "editor.el_user_defined"
10848   },
10849   {
10850     TYPE_SWITCH,
10851     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10852   },
10853   {
10854     TYPE_SWITCH,
10855     &setup.editor.el_headlines,                 "editor.el_headlines"
10856   },
10857   {
10858     TYPE_SWITCH,
10859     &setup.editor.show_element_token,           "editor.show_element_token"
10860   },
10861   {
10862     TYPE_SWITCH,
10863     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10864   },
10865 };
10866
10867 static struct TokenInfo editor_cascade_setup_tokens[] =
10868 {
10869   {
10870     TYPE_SWITCH,
10871     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10872   },
10873   {
10874     TYPE_SWITCH,
10875     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10876   },
10877   {
10878     TYPE_SWITCH,
10879     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10880   },
10881   {
10882     TYPE_SWITCH,
10883     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10884   },
10885   {
10886     TYPE_SWITCH,
10887     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10888   },
10889   {
10890     TYPE_SWITCH,
10891     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10892   },
10893   {
10894     TYPE_SWITCH,
10895     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10896   },
10897   {
10898     TYPE_SWITCH,
10899     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10900   },
10901   {
10902     TYPE_SWITCH,
10903     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10904   },
10905   {
10906     TYPE_SWITCH,
10907     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10908   },
10909   {
10910     TYPE_SWITCH,
10911     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10912   },
10913   {
10914     TYPE_SWITCH,
10915     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10916   },
10917   {
10918     TYPE_SWITCH,
10919     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10920   },
10921   {
10922     TYPE_SWITCH,
10923     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10924   },
10925   {
10926     TYPE_SWITCH,
10927     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10928   },
10929   {
10930     TYPE_SWITCH,
10931     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10932   },
10933   {
10934     TYPE_SWITCH,
10935     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10936   },
10937   {
10938     TYPE_SWITCH,
10939     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10940   },
10941   {
10942     TYPE_SWITCH,
10943     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10944   },
10945   {
10946     TYPE_SWITCH,
10947     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10948   },
10949 };
10950
10951 static struct TokenInfo shortcut_setup_tokens[] =
10952 {
10953   {
10954     TYPE_KEY_X11,
10955     &setup.shortcut.save_game,                  "shortcut.save_game"
10956   },
10957   {
10958     TYPE_KEY_X11,
10959     &setup.shortcut.load_game,                  "shortcut.load_game"
10960   },
10961   {
10962     TYPE_KEY_X11,
10963     &setup.shortcut.restart_game,               "shortcut.restart_game"
10964   },
10965   {
10966     TYPE_KEY_X11,
10967     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10968   },
10969   {
10970     TYPE_KEY_X11,
10971     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10972   },
10973   {
10974     TYPE_KEY_X11,
10975     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10976   },
10977   {
10978     TYPE_KEY_X11,
10979     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10980   },
10981   {
10982     TYPE_KEY_X11,
10983     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10984   },
10985   {
10986     TYPE_KEY_X11,
10987     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10988   },
10989   {
10990     TYPE_KEY_X11,
10991     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10992   },
10993   {
10994     TYPE_KEY_X11,
10995     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10996   },
10997   {
10998     TYPE_KEY_X11,
10999     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
11000   },
11001   {
11002     TYPE_KEY_X11,
11003     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
11004   },
11005   {
11006     TYPE_KEY_X11,
11007     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
11008   },
11009   {
11010     TYPE_KEY_X11,
11011     &setup.shortcut.tape_record,                "shortcut.tape_record"
11012   },
11013   {
11014     TYPE_KEY_X11,
11015     &setup.shortcut.tape_play,                  "shortcut.tape_play"
11016   },
11017   {
11018     TYPE_KEY_X11,
11019     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
11020   },
11021   {
11022     TYPE_KEY_X11,
11023     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
11024   },
11025   {
11026     TYPE_KEY_X11,
11027     &setup.shortcut.sound_music,                "shortcut.sound_music"
11028   },
11029   {
11030     TYPE_KEY_X11,
11031     &setup.shortcut.snap_left,                  "shortcut.snap_left"
11032   },
11033   {
11034     TYPE_KEY_X11,
11035     &setup.shortcut.snap_right,                 "shortcut.snap_right"
11036   },
11037   {
11038     TYPE_KEY_X11,
11039     &setup.shortcut.snap_up,                    "shortcut.snap_up"
11040   },
11041   {
11042     TYPE_KEY_X11,
11043     &setup.shortcut.snap_down,                  "shortcut.snap_down"
11044   },
11045 };
11046
11047 static struct SetupInputInfo setup_input;
11048 static struct TokenInfo player_setup_tokens[] =
11049 {
11050   {
11051     TYPE_BOOLEAN,
11052     &setup_input.use_joystick,                  ".use_joystick"
11053   },
11054   {
11055     TYPE_STRING,
11056     &setup_input.joy.device_name,               ".joy.device_name"
11057   },
11058   {
11059     TYPE_INTEGER,
11060     &setup_input.joy.xleft,                     ".joy.xleft"
11061   },
11062   {
11063     TYPE_INTEGER,
11064     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
11065   },
11066   {
11067     TYPE_INTEGER,
11068     &setup_input.joy.xright,                    ".joy.xright"
11069   },
11070   {
11071     TYPE_INTEGER,
11072     &setup_input.joy.yupper,                    ".joy.yupper"
11073   },
11074   {
11075     TYPE_INTEGER,
11076     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
11077   },
11078   {
11079     TYPE_INTEGER,
11080     &setup_input.joy.ylower,                    ".joy.ylower"
11081   },
11082   {
11083     TYPE_INTEGER,
11084     &setup_input.joy.snap,                      ".joy.snap_field"
11085   },
11086   {
11087     TYPE_INTEGER,
11088     &setup_input.joy.drop,                      ".joy.place_bomb"
11089   },
11090   {
11091     TYPE_KEY_X11,
11092     &setup_input.key.left,                      ".key.move_left"
11093   },
11094   {
11095     TYPE_KEY_X11,
11096     &setup_input.key.right,                     ".key.move_right"
11097   },
11098   {
11099     TYPE_KEY_X11,
11100     &setup_input.key.up,                        ".key.move_up"
11101   },
11102   {
11103     TYPE_KEY_X11,
11104     &setup_input.key.down,                      ".key.move_down"
11105   },
11106   {
11107     TYPE_KEY_X11,
11108     &setup_input.key.snap,                      ".key.snap_field"
11109   },
11110   {
11111     TYPE_KEY_X11,
11112     &setup_input.key.drop,                      ".key.place_bomb"
11113   },
11114 };
11115
11116 static struct TokenInfo system_setup_tokens[] =
11117 {
11118   {
11119     TYPE_STRING,
11120     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
11121   },
11122   {
11123     TYPE_STRING,
11124     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
11125   },
11126   {
11127     TYPE_STRING,
11128     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
11129   },
11130   {
11131     TYPE_INTEGER,
11132     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
11133   },
11134 };
11135
11136 static struct TokenInfo internal_setup_tokens[] =
11137 {
11138   {
11139     TYPE_STRING,
11140     &setup.internal.program_title,              "program_title"
11141   },
11142   {
11143     TYPE_STRING,
11144     &setup.internal.program_version,            "program_version"
11145   },
11146   {
11147     TYPE_STRING,
11148     &setup.internal.program_author,             "program_author"
11149   },
11150   {
11151     TYPE_STRING,
11152     &setup.internal.program_email,              "program_email"
11153   },
11154   {
11155     TYPE_STRING,
11156     &setup.internal.program_website,            "program_website"
11157   },
11158   {
11159     TYPE_STRING,
11160     &setup.internal.program_copyright,          "program_copyright"
11161   },
11162   {
11163     TYPE_STRING,
11164     &setup.internal.program_company,            "program_company"
11165   },
11166   {
11167     TYPE_STRING,
11168     &setup.internal.program_icon_file,          "program_icon_file"
11169   },
11170   {
11171     TYPE_STRING,
11172     &setup.internal.default_graphics_set,       "default_graphics_set"
11173   },
11174   {
11175     TYPE_STRING,
11176     &setup.internal.default_sounds_set,         "default_sounds_set"
11177   },
11178   {
11179     TYPE_STRING,
11180     &setup.internal.default_music_set,          "default_music_set"
11181   },
11182   {
11183     TYPE_STRING,
11184     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11185   },
11186   {
11187     TYPE_STRING,
11188     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11189   },
11190   {
11191     TYPE_STRING,
11192     &setup.internal.fallback_music_file,        "fallback_music_file"
11193   },
11194   {
11195     TYPE_STRING,
11196     &setup.internal.default_level_series,       "default_level_series"
11197   },
11198   {
11199     TYPE_INTEGER,
11200     &setup.internal.default_window_width,       "default_window_width"
11201   },
11202   {
11203     TYPE_INTEGER,
11204     &setup.internal.default_window_height,      "default_window_height"
11205   },
11206   {
11207     TYPE_BOOLEAN,
11208     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11209   },
11210   {
11211     TYPE_BOOLEAN,
11212     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11213   },
11214   {
11215     TYPE_BOOLEAN,
11216     &setup.internal.create_user_levelset,       "create_user_levelset"
11217   },
11218   {
11219     TYPE_BOOLEAN,
11220     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11221   },
11222   {
11223     TYPE_BOOLEAN,
11224     &setup.internal.menu_game,                  "menu_game"
11225   },
11226   {
11227     TYPE_BOOLEAN,
11228     &setup.internal.menu_engines,               "menu_engines"
11229   },
11230   {
11231     TYPE_BOOLEAN,
11232     &setup.internal.menu_editor,                "menu_editor"
11233   },
11234   {
11235     TYPE_BOOLEAN,
11236     &setup.internal.menu_graphics,              "menu_graphics"
11237   },
11238   {
11239     TYPE_BOOLEAN,
11240     &setup.internal.menu_sound,                 "menu_sound"
11241   },
11242   {
11243     TYPE_BOOLEAN,
11244     &setup.internal.menu_artwork,               "menu_artwork"
11245   },
11246   {
11247     TYPE_BOOLEAN,
11248     &setup.internal.menu_input,                 "menu_input"
11249   },
11250   {
11251     TYPE_BOOLEAN,
11252     &setup.internal.menu_touch,                 "menu_touch"
11253   },
11254   {
11255     TYPE_BOOLEAN,
11256     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11257   },
11258   {
11259     TYPE_BOOLEAN,
11260     &setup.internal.menu_exit,                  "menu_exit"
11261   },
11262   {
11263     TYPE_BOOLEAN,
11264     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11265   },
11266   {
11267     TYPE_BOOLEAN,
11268     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11269   },
11270   {
11271     TYPE_BOOLEAN,
11272     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11273   },
11274   {
11275     TYPE_BOOLEAN,
11276     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11277   },
11278   {
11279     TYPE_BOOLEAN,
11280     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11281   },
11282   {
11283     TYPE_BOOLEAN,
11284     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11285   },
11286   {
11287     TYPE_BOOLEAN,
11288     &setup.internal.info_title,                 "info_title"
11289   },
11290   {
11291     TYPE_BOOLEAN,
11292     &setup.internal.info_elements,              "info_elements"
11293   },
11294   {
11295     TYPE_BOOLEAN,
11296     &setup.internal.info_music,                 "info_music"
11297   },
11298   {
11299     TYPE_BOOLEAN,
11300     &setup.internal.info_credits,               "info_credits"
11301   },
11302   {
11303     TYPE_BOOLEAN,
11304     &setup.internal.info_program,               "info_program"
11305   },
11306   {
11307     TYPE_BOOLEAN,
11308     &setup.internal.info_version,               "info_version"
11309   },
11310   {
11311     TYPE_BOOLEAN,
11312     &setup.internal.info_levelset,              "info_levelset"
11313   },
11314   {
11315     TYPE_BOOLEAN,
11316     &setup.internal.info_exit,                  "info_exit"
11317   },
11318 };
11319
11320 static struct TokenInfo debug_setup_tokens[] =
11321 {
11322   {
11323     TYPE_INTEGER,
11324     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11325   },
11326   {
11327     TYPE_INTEGER,
11328     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11329   },
11330   {
11331     TYPE_INTEGER,
11332     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11333   },
11334   {
11335     TYPE_INTEGER,
11336     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11337   },
11338   {
11339     TYPE_INTEGER,
11340     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11341   },
11342   {
11343     TYPE_INTEGER,
11344     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11345   },
11346   {
11347     TYPE_INTEGER,
11348     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11349   },
11350   {
11351     TYPE_INTEGER,
11352     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11353   },
11354   {
11355     TYPE_INTEGER,
11356     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11357   },
11358   {
11359     TYPE_INTEGER,
11360     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11361   },
11362   {
11363     TYPE_KEY_X11,
11364     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11365   },
11366   {
11367     TYPE_KEY_X11,
11368     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11369   },
11370   {
11371     TYPE_KEY_X11,
11372     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11373   },
11374   {
11375     TYPE_KEY_X11,
11376     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11377   },
11378   {
11379     TYPE_KEY_X11,
11380     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11381   },
11382   {
11383     TYPE_KEY_X11,
11384     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11385   },
11386   {
11387     TYPE_KEY_X11,
11388     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11389   },
11390   {
11391     TYPE_KEY_X11,
11392     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11393   },
11394   {
11395     TYPE_KEY_X11,
11396     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11397   },
11398   {
11399     TYPE_KEY_X11,
11400     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11401   },
11402   {
11403     TYPE_BOOLEAN,
11404     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11405   {
11406     TYPE_BOOLEAN,
11407     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11408   },
11409   {
11410     TYPE_BOOLEAN,
11411     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11412   },
11413   {
11414     TYPE_SWITCH3,
11415     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11416   },
11417   {
11418     TYPE_INTEGER,
11419     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11420   },
11421 };
11422
11423 static struct TokenInfo options_setup_tokens[] =
11424 {
11425   {
11426     TYPE_BOOLEAN,
11427     &setup.options.verbose,                     "options.verbose"
11428   },
11429   {
11430     TYPE_BOOLEAN,
11431     &setup.options.debug,                       "options.debug"
11432   },
11433   {
11434     TYPE_STRING,
11435     &setup.options.debug_mode,                  "options.debug_mode"
11436   },
11437 };
11438
11439 static void setSetupInfoToDefaults(struct SetupInfo *si)
11440 {
11441   int i;
11442
11443   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11444
11445   si->multiple_users = TRUE;
11446
11447   si->sound = TRUE;
11448   si->sound_loops = TRUE;
11449   si->sound_music = TRUE;
11450   si->sound_simple = TRUE;
11451   si->toons = TRUE;
11452   si->global_animations = TRUE;
11453   si->scroll_delay = TRUE;
11454   si->forced_scroll_delay = FALSE;
11455   si->scroll_delay_value = STD_SCROLL_DELAY;
11456   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11457   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11458   si->fade_screens = TRUE;
11459   si->autorecord = TRUE;
11460   si->autorecord_after_replay = TRUE;
11461   si->auto_pause_on_start = FALSE;
11462   si->show_titlescreen = TRUE;
11463   si->quick_doors = FALSE;
11464   si->team_mode = FALSE;
11465   si->handicap = TRUE;
11466   si->skip_levels = TRUE;
11467   si->increment_levels = TRUE;
11468   si->auto_play_next_level = TRUE;
11469   si->count_score_after_game = TRUE;
11470   si->show_scores_after_game = TRUE;
11471   si->time_limit = TRUE;
11472   si->fullscreen = FALSE;
11473   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11474   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11475   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11476   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11477   si->ask_on_escape = TRUE;
11478   si->ask_on_escape_editor = TRUE;
11479   si->ask_on_game_over = TRUE;
11480   si->ask_on_quit_game = TRUE;
11481   si->ask_on_quit_program = TRUE;
11482   si->quick_switch = FALSE;
11483   si->input_on_focus = FALSE;
11484   si->prefer_aga_graphics = TRUE;
11485   si->prefer_lowpass_sounds = FALSE;
11486   si->prefer_extra_panel_items = TRUE;
11487   si->game_speed_extended = FALSE;
11488   si->game_frame_delay = GAME_FRAME_DELAY;
11489   si->bd_skip_uncovering = FALSE;
11490   si->bd_skip_hatching = FALSE;
11491   si->bd_scroll_delay = TRUE;
11492   si->bd_smooth_movements = AUTO;
11493   si->sp_show_border_elements = FALSE;
11494   si->small_game_graphics = FALSE;
11495   si->show_load_save_buttons = FALSE;
11496   si->show_undo_redo_buttons = FALSE;
11497   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11498
11499   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11500   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11501   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11502
11503   si->override_level_graphics = FALSE;
11504   si->override_level_sounds = FALSE;
11505   si->override_level_music = FALSE;
11506
11507   si->volume_simple = 100;              // percent
11508   si->volume_loops = 100;               // percent
11509   si->volume_music = 100;               // percent
11510
11511   si->network_mode = FALSE;
11512   si->network_player_nr = 0;            // first player
11513   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11514
11515   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11516   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11517   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11518   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11519   si->touch.draw_outlined = TRUE;
11520   si->touch.draw_pressed = TRUE;
11521
11522   for (i = 0; i < 2; i++)
11523   {
11524     char *default_grid_button[6][2] =
11525     {
11526       { "      ", "  ^^  " },
11527       { "      ", "  ^^  " },
11528       { "      ", "<<  >>" },
11529       { "      ", "<<  >>" },
11530       { "111222", "  vv  " },
11531       { "111222", "  vv  " }
11532     };
11533     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11534     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11535     int min_xsize = MIN(6, grid_xsize);
11536     int min_ysize = MIN(6, grid_ysize);
11537     int startx = grid_xsize - min_xsize;
11538     int starty = grid_ysize - min_ysize;
11539     int x, y;
11540
11541     // virtual buttons grid can only be set to defaults if video is initialized
11542     // (this will be repeated if virtual buttons are not loaded from setup file)
11543     if (video.initialized)
11544     {
11545       si->touch.grid_xsize[i] = grid_xsize;
11546       si->touch.grid_ysize[i] = grid_ysize;
11547     }
11548     else
11549     {
11550       si->touch.grid_xsize[i] = -1;
11551       si->touch.grid_ysize[i] = -1;
11552     }
11553
11554     for (x = 0; x < MAX_GRID_XSIZE; x++)
11555       for (y = 0; y < MAX_GRID_YSIZE; y++)
11556         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11557
11558     for (x = 0; x < min_xsize; x++)
11559       for (y = 0; y < min_ysize; y++)
11560         si->touch.grid_button[i][x][starty + y] =
11561           default_grid_button[y][0][x];
11562
11563     for (x = 0; x < min_xsize; x++)
11564       for (y = 0; y < min_ysize; y++)
11565         si->touch.grid_button[i][startx + x][starty + y] =
11566           default_grid_button[y][1][x];
11567   }
11568
11569   si->touch.grid_initialized            = video.initialized;
11570
11571   si->touch.overlay_buttons             = FALSE;
11572
11573   si->editor.el_boulderdash             = TRUE;
11574   si->editor.el_boulderdash_native      = TRUE;
11575   si->editor.el_boulderdash_effects     = TRUE;
11576   si->editor.el_emerald_mine            = TRUE;
11577   si->editor.el_emerald_mine_club       = TRUE;
11578   si->editor.el_more                    = TRUE;
11579   si->editor.el_sokoban                 = TRUE;
11580   si->editor.el_supaplex                = TRUE;
11581   si->editor.el_diamond_caves           = TRUE;
11582   si->editor.el_dx_boulderdash          = TRUE;
11583
11584   si->editor.el_mirror_magic            = TRUE;
11585   si->editor.el_deflektor               = TRUE;
11586
11587   si->editor.el_chars                   = TRUE;
11588   si->editor.el_steel_chars             = TRUE;
11589
11590   si->editor.el_classic                 = TRUE;
11591   si->editor.el_custom                  = TRUE;
11592
11593   si->editor.el_user_defined            = FALSE;
11594   si->editor.el_dynamic                 = TRUE;
11595
11596   si->editor.el_headlines               = TRUE;
11597
11598   si->editor.show_element_token         = FALSE;
11599
11600   si->editor.show_read_only_warning     = TRUE;
11601
11602   si->editor.use_template_for_new_levels = TRUE;
11603
11604   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11605   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11606   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11607   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11608   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11609
11610   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11611   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11612   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11613   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11614   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11615
11616   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11617   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11618   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11619   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11620   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11621   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11622
11623   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11624   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11625   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11626
11627   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11628   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11629   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11630   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11631
11632   for (i = 0; i < MAX_PLAYERS; i++)
11633   {
11634     si->input[i].use_joystick = FALSE;
11635     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11636     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11637     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11638     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11639     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11640     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11641     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11642     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11643     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11644     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11645     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11646     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11647     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11648     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11649     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11650   }
11651
11652   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11653   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11654   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11655   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11656
11657   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11658   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11659   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11660   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11661   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11662   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11663   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11664
11665   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11666
11667   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11668   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11669   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11670
11671   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11672   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11673   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11674
11675   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11676   si->internal.choose_from_top_leveldir = FALSE;
11677   si->internal.show_scaling_in_title = TRUE;
11678   si->internal.create_user_levelset = TRUE;
11679   si->internal.info_screens_from_main = FALSE;
11680
11681   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11682   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11683
11684   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11685   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11686   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11687   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11688   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11689   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11690   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11691   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11692   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11693   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11694
11695   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11696   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11697   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11698   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11699   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11700   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11701   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11702   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11703   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11704   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11705
11706   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11707   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11708
11709   si->debug.show_frames_per_second = FALSE;
11710
11711   si->debug.xsn_mode = AUTO;
11712   si->debug.xsn_percent = 0;
11713
11714   si->options.verbose = FALSE;
11715   si->options.debug = FALSE;
11716   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11717
11718 #if defined(PLATFORM_ANDROID)
11719   si->fullscreen = TRUE;
11720   si->touch.overlay_buttons = TRUE;
11721 #endif
11722
11723   setHideSetupEntry(&setup.debug.xsn_mode);
11724 }
11725
11726 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11727 {
11728   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11729 }
11730
11731 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11732 {
11733   si->player_uuid = NULL;       // (will be set later)
11734   si->player_version = 1;       // (will be set later)
11735
11736   si->use_api_server = TRUE;
11737   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11738   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11739   si->ask_for_uploading_tapes = TRUE;
11740   si->ask_for_remaining_tapes = FALSE;
11741   si->provide_uploading_tapes = TRUE;
11742   si->ask_for_using_api_server = TRUE;
11743   si->has_remaining_tapes = FALSE;
11744 }
11745
11746 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11747 {
11748   si->editor_cascade.el_bd              = TRUE;
11749   si->editor_cascade.el_bd_native       = TRUE;
11750   si->editor_cascade.el_bd_effects      = FALSE;
11751   si->editor_cascade.el_em              = TRUE;
11752   si->editor_cascade.el_emc             = TRUE;
11753   si->editor_cascade.el_rnd             = TRUE;
11754   si->editor_cascade.el_sb              = TRUE;
11755   si->editor_cascade.el_sp              = TRUE;
11756   si->editor_cascade.el_dc              = TRUE;
11757   si->editor_cascade.el_dx              = TRUE;
11758
11759   si->editor_cascade.el_mm              = TRUE;
11760   si->editor_cascade.el_df              = TRUE;
11761
11762   si->editor_cascade.el_chars           = FALSE;
11763   si->editor_cascade.el_steel_chars     = FALSE;
11764   si->editor_cascade.el_ce              = FALSE;
11765   si->editor_cascade.el_ge              = FALSE;
11766   si->editor_cascade.el_es              = FALSE;
11767   si->editor_cascade.el_ref             = FALSE;
11768   si->editor_cascade.el_user            = FALSE;
11769   si->editor_cascade.el_dynamic         = FALSE;
11770 }
11771
11772 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11773
11774 static char *getHideSetupToken(void *setup_value)
11775 {
11776   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11777
11778   if (setup_value != NULL)
11779     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11780
11781   return hide_setup_token;
11782 }
11783
11784 void setHideSetupEntry(void *setup_value)
11785 {
11786   char *hide_setup_token = getHideSetupToken(setup_value);
11787
11788   if (hide_setup_hash == NULL)
11789     hide_setup_hash = newSetupFileHash();
11790
11791   if (setup_value != NULL)
11792     setHashEntry(hide_setup_hash, hide_setup_token, "");
11793 }
11794
11795 void removeHideSetupEntry(void *setup_value)
11796 {
11797   char *hide_setup_token = getHideSetupToken(setup_value);
11798
11799   if (setup_value != NULL)
11800     removeHashEntry(hide_setup_hash, hide_setup_token);
11801 }
11802
11803 boolean hideSetupEntry(void *setup_value)
11804 {
11805   char *hide_setup_token = getHideSetupToken(setup_value);
11806
11807   return (setup_value != NULL &&
11808           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11809 }
11810
11811 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11812                                       struct TokenInfo *token_info,
11813                                       int token_nr, char *token_text)
11814 {
11815   char *token_hide_text = getStringCat2(token_text, ".hide");
11816   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11817
11818   // set the value of this setup option in the setup option structure
11819   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11820
11821   // check if this setup option should be hidden in the setup menu
11822   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11823     setHideSetupEntry(token_info[token_nr].value);
11824
11825   free(token_hide_text);
11826 }
11827
11828 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11829                                       struct TokenInfo *token_info,
11830                                       int token_nr)
11831 {
11832   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11833                             token_info[token_nr].text);
11834 }
11835
11836 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11837 {
11838   int i, pnr;
11839
11840   if (!setup_file_hash)
11841     return;
11842
11843   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11844     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11845
11846   setup.touch.grid_initialized = TRUE;
11847   for (i = 0; i < 2; i++)
11848   {
11849     int grid_xsize = setup.touch.grid_xsize[i];
11850     int grid_ysize = setup.touch.grid_ysize[i];
11851     int x, y;
11852
11853     // if virtual buttons are not loaded from setup file, repeat initializing
11854     // virtual buttons grid with default values later when video is initialized
11855     if (grid_xsize == -1 ||
11856         grid_ysize == -1)
11857     {
11858       setup.touch.grid_initialized = FALSE;
11859
11860       continue;
11861     }
11862
11863     for (y = 0; y < grid_ysize; y++)
11864     {
11865       char token_string[MAX_LINE_LEN];
11866
11867       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11868
11869       char *value_string = getHashEntry(setup_file_hash, token_string);
11870
11871       if (value_string == NULL)
11872         continue;
11873
11874       for (x = 0; x < grid_xsize; x++)
11875       {
11876         char c = value_string[x];
11877
11878         setup.touch.grid_button[i][x][y] =
11879           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11880       }
11881     }
11882   }
11883
11884   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11885     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11886
11887   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11888     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11889
11890   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11891   {
11892     char prefix[30];
11893
11894     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11895
11896     setup_input = setup.input[pnr];
11897     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11898     {
11899       char full_token[100];
11900
11901       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11902       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11903                                 full_token);
11904     }
11905     setup.input[pnr] = setup_input;
11906   }
11907
11908   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11909     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11910
11911   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11912     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11913
11914   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11915     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11916
11917   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11918     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11919
11920   setHideRelatedSetupEntries();
11921 }
11922
11923 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11924 {
11925   int i;
11926
11927   if (!setup_file_hash)
11928     return;
11929
11930   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11931     setSetupInfo(auto_setup_tokens, i,
11932                  getHashEntry(setup_file_hash,
11933                               auto_setup_tokens[i].text));
11934 }
11935
11936 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11937 {
11938   int i;
11939
11940   if (!setup_file_hash)
11941     return;
11942
11943   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11944     setSetupInfo(server_setup_tokens, i,
11945                  getHashEntry(setup_file_hash,
11946                               server_setup_tokens[i].text));
11947 }
11948
11949 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11950 {
11951   int i;
11952
11953   if (!setup_file_hash)
11954     return;
11955
11956   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11957     setSetupInfo(editor_cascade_setup_tokens, i,
11958                  getHashEntry(setup_file_hash,
11959                               editor_cascade_setup_tokens[i].text));
11960 }
11961
11962 void LoadUserNames(void)
11963 {
11964   int last_user_nr = user.nr;
11965   int i;
11966
11967   if (global.user_names != NULL)
11968   {
11969     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11970       checked_free(global.user_names[i]);
11971
11972     checked_free(global.user_names);
11973   }
11974
11975   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11976
11977   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11978   {
11979     user.nr = i;
11980
11981     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11982
11983     if (setup_file_hash)
11984     {
11985       char *player_name = getHashEntry(setup_file_hash, "player_name");
11986
11987       global.user_names[i] = getFixedUserName(player_name);
11988
11989       freeSetupFileHash(setup_file_hash);
11990     }
11991
11992     if (global.user_names[i] == NULL)
11993       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11994   }
11995
11996   user.nr = last_user_nr;
11997 }
11998
11999 void LoadSetupFromFilename(char *filename)
12000 {
12001   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
12002
12003   if (setup_file_hash)
12004   {
12005     decodeSetupFileHash_Default(setup_file_hash);
12006
12007     freeSetupFileHash(setup_file_hash);
12008   }
12009   else
12010   {
12011     Debug("setup", "using default setup values");
12012   }
12013 }
12014
12015 static void LoadSetup_SpecialPostProcessing(void)
12016 {
12017   char *player_name_new;
12018
12019   // needed to work around problems with fixed length strings
12020   player_name_new = getFixedUserName(setup.player_name);
12021   free(setup.player_name);
12022   setup.player_name = player_name_new;
12023
12024   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
12025   if (setup.scroll_delay == FALSE)
12026   {
12027     setup.scroll_delay_value = MIN_SCROLL_DELAY;
12028     setup.scroll_delay = TRUE;                  // now always "on"
12029   }
12030
12031   // make sure that scroll delay value stays inside valid range
12032   setup.scroll_delay_value =
12033     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
12034 }
12035
12036 void LoadSetup_Default(void)
12037 {
12038   char *filename;
12039
12040   // always start with reliable default values
12041   setSetupInfoToDefaults(&setup);
12042
12043   // try to load setup values from default setup file
12044   filename = getDefaultSetupFilename();
12045
12046   if (fileExists(filename))
12047     LoadSetupFromFilename(filename);
12048
12049   // try to load setup values from platform setup file
12050   filename = getPlatformSetupFilename();
12051
12052   if (fileExists(filename))
12053     LoadSetupFromFilename(filename);
12054
12055   // try to load setup values from user setup file
12056   filename = getSetupFilename();
12057
12058   LoadSetupFromFilename(filename);
12059
12060   LoadSetup_SpecialPostProcessing();
12061 }
12062
12063 void LoadSetup_AutoSetup(void)
12064 {
12065   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12066   SetupFileHash *setup_file_hash = NULL;
12067
12068   // always start with reliable default values
12069   setSetupInfoToDefaults_AutoSetup(&setup);
12070
12071   setup_file_hash = loadSetupFileHash(filename);
12072
12073   if (setup_file_hash)
12074   {
12075     decodeSetupFileHash_AutoSetup(setup_file_hash);
12076
12077     freeSetupFileHash(setup_file_hash);
12078   }
12079
12080   free(filename);
12081 }
12082
12083 void LoadSetup_ServerSetup(void)
12084 {
12085   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12086   SetupFileHash *setup_file_hash = NULL;
12087
12088   // always start with reliable default values
12089   setSetupInfoToDefaults_ServerSetup(&setup);
12090
12091   setup_file_hash = loadSetupFileHash(filename);
12092
12093   if (setup_file_hash)
12094   {
12095     decodeSetupFileHash_ServerSetup(setup_file_hash);
12096
12097     freeSetupFileHash(setup_file_hash);
12098   }
12099
12100   free(filename);
12101
12102   if (setup.player_uuid == NULL)
12103   {
12104     // player UUID does not yet exist in setup file
12105     setup.player_uuid = getStringCopy(getUUID());
12106     setup.player_version = 2;
12107
12108     SaveSetup_ServerSetup();
12109   }
12110 }
12111
12112 void LoadSetup_EditorCascade(void)
12113 {
12114   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12115   SetupFileHash *setup_file_hash = NULL;
12116
12117   // always start with reliable default values
12118   setSetupInfoToDefaults_EditorCascade(&setup);
12119
12120   setup_file_hash = loadSetupFileHash(filename);
12121
12122   if (setup_file_hash)
12123   {
12124     decodeSetupFileHash_EditorCascade(setup_file_hash);
12125
12126     freeSetupFileHash(setup_file_hash);
12127   }
12128
12129   free(filename);
12130 }
12131
12132 void LoadSetup(void)
12133 {
12134   LoadSetup_Default();
12135   LoadSetup_AutoSetup();
12136   LoadSetup_ServerSetup();
12137   LoadSetup_EditorCascade();
12138 }
12139
12140 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
12141                                            char *mapping_line)
12142 {
12143   char mapping_guid[MAX_LINE_LEN];
12144   char *mapping_start, *mapping_end;
12145
12146   // get GUID from game controller mapping line: copy complete line
12147   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
12148   mapping_guid[MAX_LINE_LEN - 1] = '\0';
12149
12150   // get GUID from game controller mapping line: cut after GUID part
12151   mapping_start = strchr(mapping_guid, ',');
12152   if (mapping_start != NULL)
12153     *mapping_start = '\0';
12154
12155   // cut newline from game controller mapping line
12156   mapping_end = strchr(mapping_line, '\n');
12157   if (mapping_end != NULL)
12158     *mapping_end = '\0';
12159
12160   // add mapping entry to game controller mappings hash
12161   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12162 }
12163
12164 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12165                                                  char *filename)
12166 {
12167   FILE *file;
12168
12169   if (!(file = fopen(filename, MODE_READ)))
12170   {
12171     Warn("cannot read game controller mappings file '%s'", filename);
12172
12173     return;
12174   }
12175
12176   while (!feof(file))
12177   {
12178     char line[MAX_LINE_LEN];
12179
12180     if (!fgets(line, MAX_LINE_LEN, file))
12181       break;
12182
12183     addGameControllerMappingToHash(mappings_hash, line);
12184   }
12185
12186   fclose(file);
12187 }
12188
12189 void SaveSetup_Default(void)
12190 {
12191   char *filename = getSetupFilename();
12192   FILE *file;
12193   int i, pnr;
12194
12195   InitUserDataDirectory();
12196
12197   if (!(file = fopen(filename, MODE_WRITE)))
12198   {
12199     Warn("cannot write setup file '%s'", filename);
12200
12201     return;
12202   }
12203
12204   fprintFileHeader(file, SETUP_FILENAME);
12205
12206   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12207   {
12208     // just to make things nicer :)
12209     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12210         global_setup_tokens[i].value == &setup.sound                    ||
12211         global_setup_tokens[i].value == &setup.graphics_set             ||
12212         global_setup_tokens[i].value == &setup.volume_simple            ||
12213         global_setup_tokens[i].value == &setup.network_mode             ||
12214         global_setup_tokens[i].value == &setup.touch.control_type       ||
12215         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12216         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12217       fprintf(file, "\n");
12218
12219     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12220   }
12221
12222   for (i = 0; i < 2; i++)
12223   {
12224     int grid_xsize = setup.touch.grid_xsize[i];
12225     int grid_ysize = setup.touch.grid_ysize[i];
12226     int x, y;
12227
12228     fprintf(file, "\n");
12229
12230     for (y = 0; y < grid_ysize; y++)
12231     {
12232       char token_string[MAX_LINE_LEN];
12233       char value_string[MAX_LINE_LEN];
12234
12235       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12236
12237       for (x = 0; x < grid_xsize; x++)
12238       {
12239         char c = setup.touch.grid_button[i][x][y];
12240
12241         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12242       }
12243
12244       value_string[grid_xsize] = '\0';
12245
12246       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12247     }
12248   }
12249
12250   fprintf(file, "\n");
12251   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12252     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12253
12254   fprintf(file, "\n");
12255   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12256     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12257
12258   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12259   {
12260     char prefix[30];
12261
12262     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12263     fprintf(file, "\n");
12264
12265     setup_input = setup.input[pnr];
12266     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12267       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12268   }
12269
12270   fprintf(file, "\n");
12271   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12272     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12273
12274   // (internal setup values not saved to user setup file)
12275
12276   fprintf(file, "\n");
12277   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12278     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12279         setup.debug.xsn_mode != AUTO)
12280       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12281
12282   fprintf(file, "\n");
12283   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12284     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12285
12286   fclose(file);
12287
12288   SetFilePermissions(filename, PERMS_PRIVATE);
12289 }
12290
12291 void SaveSetup_AutoSetup(void)
12292 {
12293   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12294   FILE *file;
12295   int i;
12296
12297   InitUserDataDirectory();
12298
12299   if (!(file = fopen(filename, MODE_WRITE)))
12300   {
12301     Warn("cannot write auto setup file '%s'", filename);
12302
12303     free(filename);
12304
12305     return;
12306   }
12307
12308   fprintFileHeader(file, AUTOSETUP_FILENAME);
12309
12310   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12311     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12312
12313   fclose(file);
12314
12315   SetFilePermissions(filename, PERMS_PRIVATE);
12316
12317   free(filename);
12318 }
12319
12320 void SaveSetup_ServerSetup(void)
12321 {
12322   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12323   FILE *file;
12324   int i;
12325
12326   InitUserDataDirectory();
12327
12328   if (!(file = fopen(filename, MODE_WRITE)))
12329   {
12330     Warn("cannot write server setup file '%s'", filename);
12331
12332     free(filename);
12333
12334     return;
12335   }
12336
12337   fprintFileHeader(file, SERVERSETUP_FILENAME);
12338
12339   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12340   {
12341     // just to make things nicer :)
12342     if (server_setup_tokens[i].value == &setup.use_api_server)
12343       fprintf(file, "\n");
12344
12345     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12346   }
12347
12348   fclose(file);
12349
12350   SetFilePermissions(filename, PERMS_PRIVATE);
12351
12352   free(filename);
12353 }
12354
12355 void SaveSetup_EditorCascade(void)
12356 {
12357   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12358   FILE *file;
12359   int i;
12360
12361   InitUserDataDirectory();
12362
12363   if (!(file = fopen(filename, MODE_WRITE)))
12364   {
12365     Warn("cannot write editor cascade state file '%s'", filename);
12366
12367     free(filename);
12368
12369     return;
12370   }
12371
12372   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12373
12374   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12375     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12376
12377   fclose(file);
12378
12379   SetFilePermissions(filename, PERMS_PRIVATE);
12380
12381   free(filename);
12382 }
12383
12384 void SaveSetup(void)
12385 {
12386   SaveSetup_Default();
12387   SaveSetup_AutoSetup();
12388   SaveSetup_ServerSetup();
12389   SaveSetup_EditorCascade();
12390 }
12391
12392 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12393                                                   char *filename)
12394 {
12395   FILE *file;
12396
12397   if (!(file = fopen(filename, MODE_WRITE)))
12398   {
12399     Warn("cannot write game controller mappings file '%s'", filename);
12400
12401     return;
12402   }
12403
12404   BEGIN_HASH_ITERATION(mappings_hash, itr)
12405   {
12406     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12407   }
12408   END_HASH_ITERATION(mappings_hash, itr)
12409
12410   fclose(file);
12411 }
12412
12413 void SaveSetup_AddGameControllerMapping(char *mapping)
12414 {
12415   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12416   SetupFileHash *mappings_hash = newSetupFileHash();
12417
12418   InitUserDataDirectory();
12419
12420   // load existing personal game controller mappings
12421   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12422
12423   // add new mapping to personal game controller mappings
12424   addGameControllerMappingToHash(mappings_hash, mapping);
12425
12426   // save updated personal game controller mappings
12427   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12428
12429   freeSetupFileHash(mappings_hash);
12430   free(filename);
12431 }
12432
12433 void LoadCustomElementDescriptions(void)
12434 {
12435   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12436   SetupFileHash *setup_file_hash;
12437   int i;
12438
12439   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12440   {
12441     if (element_info[i].custom_description != NULL)
12442     {
12443       free(element_info[i].custom_description);
12444       element_info[i].custom_description = NULL;
12445     }
12446   }
12447
12448   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12449     return;
12450
12451   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12452   {
12453     char *token = getStringCat2(element_info[i].token_name, ".name");
12454     char *value = getHashEntry(setup_file_hash, token);
12455
12456     if (value != NULL)
12457       element_info[i].custom_description = getStringCopy(value);
12458
12459     free(token);
12460   }
12461
12462   freeSetupFileHash(setup_file_hash);
12463 }
12464
12465 static int getElementFromToken(char *token)
12466 {
12467   char *value = getHashEntry(element_token_hash, token);
12468
12469   if (value != NULL)
12470     return atoi(value);
12471
12472   Warn("unknown element token '%s'", token);
12473
12474   return EL_UNDEFINED;
12475 }
12476
12477 void FreeGlobalAnimEventInfo(void)
12478 {
12479   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12480
12481   if (gaei->event_list == NULL)
12482     return;
12483
12484   int i;
12485
12486   for (i = 0; i < gaei->num_event_lists; i++)
12487   {
12488     checked_free(gaei->event_list[i]->event_value);
12489     checked_free(gaei->event_list[i]);
12490   }
12491
12492   checked_free(gaei->event_list);
12493
12494   gaei->event_list = NULL;
12495   gaei->num_event_lists = 0;
12496 }
12497
12498 static int AddGlobalAnimEventList(void)
12499 {
12500   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12501   int list_pos = gaei->num_event_lists++;
12502
12503   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12504                                      sizeof(struct GlobalAnimEventListInfo *));
12505
12506   gaei->event_list[list_pos] =
12507     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12508
12509   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12510
12511   gaeli->event_value = NULL;
12512   gaeli->num_event_values = 0;
12513
12514   return list_pos;
12515 }
12516
12517 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12518 {
12519   // do not add empty global animation events
12520   if (event_value == ANIM_EVENT_NONE)
12521     return list_pos;
12522
12523   // if list position is undefined, create new list
12524   if (list_pos == ANIM_EVENT_UNDEFINED)
12525     list_pos = AddGlobalAnimEventList();
12526
12527   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12528   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12529   int value_pos = gaeli->num_event_values++;
12530
12531   gaeli->event_value = checked_realloc(gaeli->event_value,
12532                                        gaeli->num_event_values * sizeof(int *));
12533
12534   gaeli->event_value[value_pos] = event_value;
12535
12536   return list_pos;
12537 }
12538
12539 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12540 {
12541   if (list_pos == ANIM_EVENT_UNDEFINED)
12542     return ANIM_EVENT_NONE;
12543
12544   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12545   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12546
12547   return gaeli->event_value[value_pos];
12548 }
12549
12550 int GetGlobalAnimEventValueCount(int list_pos)
12551 {
12552   if (list_pos == ANIM_EVENT_UNDEFINED)
12553     return 0;
12554
12555   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12556   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12557
12558   return gaeli->num_event_values;
12559 }
12560
12561 // This function checks if a string <s> of the format "string1, string2, ..."
12562 // exactly contains a string <s_contained>.
12563
12564 static boolean string_has_parameter(char *s, char *s_contained)
12565 {
12566   char *substring;
12567
12568   if (s == NULL || s_contained == NULL)
12569     return FALSE;
12570
12571   if (strlen(s_contained) > strlen(s))
12572     return FALSE;
12573
12574   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12575   {
12576     char next_char = s[strlen(s_contained)];
12577
12578     // check if next character is delimiter or whitespace
12579     if (next_char == ',' || next_char == '\0' ||
12580         next_char == ' ' || next_char == '\t')
12581       return TRUE;
12582   }
12583
12584   // check if string contains another parameter string after a comma
12585   substring = strchr(s, ',');
12586   if (substring == NULL)        // string does not contain a comma
12587     return FALSE;
12588
12589   // advance string pointer to next character after the comma
12590   substring++;
12591
12592   // skip potential whitespaces after the comma
12593   while (*substring == ' ' || *substring == '\t')
12594     substring++;
12595
12596   return string_has_parameter(substring, s_contained);
12597 }
12598
12599 static int get_anim_parameter_value_ce(char *s)
12600 {
12601   char *s_ptr = s;
12602   char *pattern_1 = "ce_change:custom_";
12603   char *pattern_2 = ".page_";
12604   int pattern_1_len = strlen(pattern_1);
12605   char *matching_char = strstr(s_ptr, pattern_1);
12606   int result = ANIM_EVENT_NONE;
12607
12608   if (matching_char == NULL)
12609     return ANIM_EVENT_NONE;
12610
12611   result = ANIM_EVENT_CE_CHANGE;
12612
12613   s_ptr = matching_char + pattern_1_len;
12614
12615   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12616   if (*s_ptr >= '0' && *s_ptr <= '9')
12617   {
12618     int gic_ce_nr = (*s_ptr++ - '0');
12619
12620     if (*s_ptr >= '0' && *s_ptr <= '9')
12621     {
12622       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12623
12624       if (*s_ptr >= '0' && *s_ptr <= '9')
12625         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12626     }
12627
12628     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12629       return ANIM_EVENT_NONE;
12630
12631     // custom element stored as 0 to 255
12632     gic_ce_nr--;
12633
12634     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12635   }
12636   else
12637   {
12638     // invalid custom element number specified
12639
12640     return ANIM_EVENT_NONE;
12641   }
12642
12643   // check for change page number ("page_X" or "page_XX") (optional)
12644   if (strPrefix(s_ptr, pattern_2))
12645   {
12646     s_ptr += strlen(pattern_2);
12647
12648     if (*s_ptr >= '0' && *s_ptr <= '9')
12649     {
12650       int gic_page_nr = (*s_ptr++ - '0');
12651
12652       if (*s_ptr >= '0' && *s_ptr <= '9')
12653         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12654
12655       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12656         return ANIM_EVENT_NONE;
12657
12658       // change page stored as 1 to 32 (0 means "all change pages")
12659
12660       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12661     }
12662     else
12663     {
12664       // invalid animation part number specified
12665
12666       return ANIM_EVENT_NONE;
12667     }
12668   }
12669
12670   // discard result if next character is neither delimiter nor whitespace
12671   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12672         *s_ptr == ' ' || *s_ptr == '\t'))
12673     return ANIM_EVENT_NONE;
12674
12675   return result;
12676 }
12677
12678 static int get_anim_parameter_value(char *s)
12679 {
12680   int event_value[] =
12681   {
12682     ANIM_EVENT_CLICK,
12683     ANIM_EVENT_INIT,
12684     ANIM_EVENT_START,
12685     ANIM_EVENT_END,
12686     ANIM_EVENT_POST
12687   };
12688   char *pattern_1[] =
12689   {
12690     "click:anim_",
12691     "init:anim_",
12692     "start:anim_",
12693     "end:anim_",
12694     "post:anim_"
12695   };
12696   char *pattern_2 = ".part_";
12697   char *matching_char = NULL;
12698   char *s_ptr = s;
12699   int pattern_1_len = 0;
12700   int result = ANIM_EVENT_NONE;
12701   int i;
12702
12703   result = get_anim_parameter_value_ce(s);
12704
12705   if (result != ANIM_EVENT_NONE)
12706     return result;
12707
12708   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12709   {
12710     matching_char = strstr(s_ptr, pattern_1[i]);
12711     pattern_1_len = strlen(pattern_1[i]);
12712     result = event_value[i];
12713
12714     if (matching_char != NULL)
12715       break;
12716   }
12717
12718   if (matching_char == NULL)
12719     return ANIM_EVENT_NONE;
12720
12721   s_ptr = matching_char + pattern_1_len;
12722
12723   // check for main animation number ("anim_X" or "anim_XX")
12724   if (*s_ptr >= '0' && *s_ptr <= '9')
12725   {
12726     int gic_anim_nr = (*s_ptr++ - '0');
12727
12728     if (*s_ptr >= '0' && *s_ptr <= '9')
12729       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12730
12731     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12732       return ANIM_EVENT_NONE;
12733
12734     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12735   }
12736   else
12737   {
12738     // invalid main animation number specified
12739
12740     return ANIM_EVENT_NONE;
12741   }
12742
12743   // check for animation part number ("part_X" or "part_XX") (optional)
12744   if (strPrefix(s_ptr, pattern_2))
12745   {
12746     s_ptr += strlen(pattern_2);
12747
12748     if (*s_ptr >= '0' && *s_ptr <= '9')
12749     {
12750       int gic_part_nr = (*s_ptr++ - '0');
12751
12752       if (*s_ptr >= '0' && *s_ptr <= '9')
12753         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12754
12755       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12756         return ANIM_EVENT_NONE;
12757
12758       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12759     }
12760     else
12761     {
12762       // invalid animation part number specified
12763
12764       return ANIM_EVENT_NONE;
12765     }
12766   }
12767
12768   // discard result if next character is neither delimiter nor whitespace
12769   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12770         *s_ptr == ' ' || *s_ptr == '\t'))
12771     return ANIM_EVENT_NONE;
12772
12773   return result;
12774 }
12775
12776 static int get_anim_parameter_values(char *s)
12777 {
12778   int list_pos = ANIM_EVENT_UNDEFINED;
12779   int event_value = ANIM_EVENT_DEFAULT;
12780
12781   if (string_has_parameter(s, "any"))
12782     event_value |= ANIM_EVENT_ANY;
12783
12784   if (string_has_parameter(s, "click:self") ||
12785       string_has_parameter(s, "click") ||
12786       string_has_parameter(s, "self"))
12787     event_value |= ANIM_EVENT_SELF;
12788
12789   if (string_has_parameter(s, "unclick:any"))
12790     event_value |= ANIM_EVENT_UNCLICK_ANY;
12791
12792   // if animation event found, add it to global animation event list
12793   if (event_value != ANIM_EVENT_NONE)
12794     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12795
12796   while (s != NULL)
12797   {
12798     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12799     event_value = get_anim_parameter_value(s);
12800
12801     // if animation event found, add it to global animation event list
12802     if (event_value != ANIM_EVENT_NONE)
12803       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12804
12805     // continue with next part of the string, starting with next comma
12806     s = strchr(s + 1, ',');
12807   }
12808
12809   return list_pos;
12810 }
12811
12812 static int get_anim_action_parameter_value(char *token)
12813 {
12814   // check most common default case first to massively speed things up
12815   if (strEqual(token, ARG_UNDEFINED))
12816     return ANIM_EVENT_ACTION_NONE;
12817
12818   int result = getImageIDFromToken(token);
12819
12820   if (result == -1)
12821   {
12822     char *gfx_token = getStringCat2("gfx.", token);
12823
12824     result = getImageIDFromToken(gfx_token);
12825
12826     checked_free(gfx_token);
12827   }
12828
12829   if (result == -1)
12830   {
12831     Key key = getKeyFromX11KeyName(token);
12832
12833     if (key != KSYM_UNDEFINED)
12834       result = -(int)key;
12835   }
12836
12837   if (result == -1)
12838   {
12839     if (isURL(token))
12840     {
12841       result = get_hash_from_string(token);     // unsigned int => int
12842       result = ABS(result);                     // may be negative now
12843       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12844
12845       setHashEntry(anim_url_hash, int2str(result, 0), token);
12846     }
12847   }
12848
12849   if (result == -1)
12850     result = ANIM_EVENT_ACTION_NONE;
12851
12852   return result;
12853 }
12854
12855 int get_parameter_value(char *value_raw, char *suffix, int type)
12856 {
12857   char *value = getStringToLower(value_raw);
12858   int result = 0;       // probably a save default value
12859
12860   if (strEqual(suffix, ".direction"))
12861   {
12862     result = (strEqual(value, "left")  ? MV_LEFT :
12863               strEqual(value, "right") ? MV_RIGHT :
12864               strEqual(value, "up")    ? MV_UP :
12865               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12866   }
12867   else if (strEqual(suffix, ".position"))
12868   {
12869     result = (strEqual(value, "left")   ? POS_LEFT :
12870               strEqual(value, "right")  ? POS_RIGHT :
12871               strEqual(value, "top")    ? POS_TOP :
12872               strEqual(value, "upper")  ? POS_UPPER :
12873               strEqual(value, "middle") ? POS_MIDDLE :
12874               strEqual(value, "lower")  ? POS_LOWER :
12875               strEqual(value, "bottom") ? POS_BOTTOM :
12876               strEqual(value, "any")    ? POS_ANY :
12877               strEqual(value, "ce")     ? POS_CE :
12878               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12879               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12880   }
12881   else if (strEqual(suffix, ".align"))
12882   {
12883     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12884               strEqual(value, "right")  ? ALIGN_RIGHT :
12885               strEqual(value, "center") ? ALIGN_CENTER :
12886               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12887   }
12888   else if (strEqual(suffix, ".valign"))
12889   {
12890     result = (strEqual(value, "top")    ? VALIGN_TOP :
12891               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12892               strEqual(value, "middle") ? VALIGN_MIDDLE :
12893               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12894   }
12895   else if (strEqual(suffix, ".anim_mode"))
12896   {
12897     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12898               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12899               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12900               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12901               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12902               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12903               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12904               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12905               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12906               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12907               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12908               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12909               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12910               string_has_parameter(value, "all")        ? ANIM_ALL :
12911               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12912               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12913               ANIM_DEFAULT);
12914
12915     if (string_has_parameter(value, "once"))
12916       result |= ANIM_ONCE;
12917
12918     if (string_has_parameter(value, "reverse"))
12919       result |= ANIM_REVERSE;
12920
12921     if (string_has_parameter(value, "opaque_player"))
12922       result |= ANIM_OPAQUE_PLAYER;
12923
12924     if (string_has_parameter(value, "static_panel"))
12925       result |= ANIM_STATIC_PANEL;
12926   }
12927   else if (strEqual(suffix, ".init_event") ||
12928            strEqual(suffix, ".anim_event"))
12929   {
12930     result = get_anim_parameter_values(value);
12931   }
12932   else if (strEqual(suffix, ".init_delay_action") ||
12933            strEqual(suffix, ".anim_delay_action") ||
12934            strEqual(suffix, ".post_delay_action") ||
12935            strEqual(suffix, ".init_event_action") ||
12936            strEqual(suffix, ".anim_event_action"))
12937   {
12938     result = get_anim_action_parameter_value(value_raw);
12939   }
12940   else if (strEqual(suffix, ".class"))
12941   {
12942     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12943               get_hash_from_string(value));
12944   }
12945   else if (strEqual(suffix, ".style"))
12946   {
12947     result = STYLE_DEFAULT;
12948
12949     if (string_has_parameter(value, "accurate_borders"))
12950       result |= STYLE_ACCURATE_BORDERS;
12951
12952     if (string_has_parameter(value, "inner_corners"))
12953       result |= STYLE_INNER_CORNERS;
12954
12955     if (string_has_parameter(value, "reverse"))
12956       result |= STYLE_REVERSE;
12957
12958     if (string_has_parameter(value, "leftmost_position"))
12959       result |= STYLE_LEFTMOST_POSITION;
12960
12961     if (string_has_parameter(value, "block_clicks"))
12962       result |= STYLE_BLOCK;
12963
12964     if (string_has_parameter(value, "passthrough_clicks"))
12965       result |= STYLE_PASSTHROUGH;
12966
12967     if (string_has_parameter(value, "multiple_actions"))
12968       result |= STYLE_MULTIPLE_ACTIONS;
12969
12970     if (string_has_parameter(value, "consume_ce_event"))
12971       result |= STYLE_CONSUME_CE_EVENT;
12972   }
12973   else if (strEqual(suffix, ".fade_mode"))
12974   {
12975     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12976               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12977               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12978               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12979               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12980               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12981               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12982               FADE_MODE_DEFAULT);
12983   }
12984   else if (strEqual(suffix, ".auto_delay_unit"))
12985   {
12986     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12987               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12988               AUTO_DELAY_UNIT_DEFAULT);
12989   }
12990   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12991   {
12992     result = gfx.get_font_from_token_function(value);
12993   }
12994   else          // generic parameter of type integer or boolean
12995   {
12996     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12997               type == TYPE_INTEGER ? get_integer_from_string(value) :
12998               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12999               ARG_UNDEFINED_VALUE);
13000   }
13001
13002   free(value);
13003
13004   return result;
13005 }
13006
13007 static int get_token_parameter_value(char *token, char *value_raw)
13008 {
13009   char *suffix;
13010
13011   if (token == NULL || value_raw == NULL)
13012     return ARG_UNDEFINED_VALUE;
13013
13014   suffix = strrchr(token, '.');
13015   if (suffix == NULL)
13016     suffix = token;
13017
13018   if (strEqual(suffix, ".element"))
13019     return getElementFromToken(value_raw);
13020
13021   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
13022   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
13023 }
13024
13025 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
13026                                      boolean ignore_defaults)
13027 {
13028   int i;
13029
13030   for (i = 0; image_config_vars[i].token != NULL; i++)
13031   {
13032     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
13033
13034     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13035     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13036       continue;
13037
13038     if (value != NULL)
13039       *image_config_vars[i].value =
13040         get_token_parameter_value(image_config_vars[i].token, value);
13041   }
13042 }
13043
13044 void InitMenuDesignSettings_Static(void)
13045 {
13046   // always start with reliable default values from static default config
13047   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
13048 }
13049
13050 static void InitMenuDesignSettings_SpecialPreProcessing(void)
13051 {
13052   int i;
13053
13054   // the following initializes hierarchical values from static configuration
13055
13056   // special case: initialize "ARG_DEFAULT" values in static default config
13057   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
13058   titlescreen_initial_first_default.fade_mode  =
13059     title_initial_first_default.fade_mode;
13060   titlescreen_initial_first_default.fade_delay =
13061     title_initial_first_default.fade_delay;
13062   titlescreen_initial_first_default.post_delay =
13063     title_initial_first_default.post_delay;
13064   titlescreen_initial_first_default.auto_delay =
13065     title_initial_first_default.auto_delay;
13066   titlescreen_initial_first_default.auto_delay_unit =
13067     title_initial_first_default.auto_delay_unit;
13068   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
13069   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
13070   titlescreen_first_default.post_delay = title_first_default.post_delay;
13071   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
13072   titlescreen_first_default.auto_delay_unit =
13073     title_first_default.auto_delay_unit;
13074   titlemessage_initial_first_default.fade_mode  =
13075     title_initial_first_default.fade_mode;
13076   titlemessage_initial_first_default.fade_delay =
13077     title_initial_first_default.fade_delay;
13078   titlemessage_initial_first_default.post_delay =
13079     title_initial_first_default.post_delay;
13080   titlemessage_initial_first_default.auto_delay =
13081     title_initial_first_default.auto_delay;
13082   titlemessage_initial_first_default.auto_delay_unit =
13083     title_initial_first_default.auto_delay_unit;
13084   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
13085   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
13086   titlemessage_first_default.post_delay = title_first_default.post_delay;
13087   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
13088   titlemessage_first_default.auto_delay_unit =
13089     title_first_default.auto_delay_unit;
13090
13091   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
13092   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
13093   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
13094   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
13095   titlescreen_initial_default.auto_delay_unit =
13096     title_initial_default.auto_delay_unit;
13097   titlescreen_default.fade_mode  = title_default.fade_mode;
13098   titlescreen_default.fade_delay = title_default.fade_delay;
13099   titlescreen_default.post_delay = title_default.post_delay;
13100   titlescreen_default.auto_delay = title_default.auto_delay;
13101   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
13102   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
13103   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
13104   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
13105   titlemessage_initial_default.auto_delay_unit =
13106     title_initial_default.auto_delay_unit;
13107   titlemessage_default.fade_mode  = title_default.fade_mode;
13108   titlemessage_default.fade_delay = title_default.fade_delay;
13109   titlemessage_default.post_delay = title_default.post_delay;
13110   titlemessage_default.auto_delay = title_default.auto_delay;
13111   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
13112
13113   // special case: initialize "ARG_DEFAULT" values in static default config
13114   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13115   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
13116   {
13117     titlescreen_initial_first[i] = titlescreen_initial_first_default;
13118     titlescreen_first[i] = titlescreen_first_default;
13119     titlemessage_initial_first[i] = titlemessage_initial_first_default;
13120     titlemessage_first[i] = titlemessage_first_default;
13121
13122     titlescreen_initial[i] = titlescreen_initial_default;
13123     titlescreen[i] = titlescreen_default;
13124     titlemessage_initial[i] = titlemessage_initial_default;
13125     titlemessage[i] = titlemessage_default;
13126   }
13127
13128   // special case: initialize "ARG_DEFAULT" values in static default config
13129   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13130   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13131   {
13132     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
13133       continue;
13134
13135     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
13136     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
13137     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
13138   }
13139
13140   // special case: initialize "ARG_DEFAULT" values in static default config
13141   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13142   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13143   {
13144     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
13145     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
13146     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
13147
13148     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
13149       continue;
13150
13151     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
13152   }
13153 }
13154
13155 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13156 {
13157   static struct
13158   {
13159     struct XY *dst, *src;
13160   }
13161   game_buttons_xy[] =
13162   {
13163     { &game.button.save,        &game.button.stop       },
13164     { &game.button.pause2,      &game.button.pause      },
13165     { &game.button.load,        &game.button.play       },
13166     { &game.button.undo,        &game.button.stop       },
13167     { &game.button.redo,        &game.button.play       },
13168
13169     { NULL,                     NULL                    }
13170   };
13171   int i, j;
13172
13173   // special case: initialize later added SETUP list size from LEVELS value
13174   if (menu.list_size[GAME_MODE_SETUP] == -1)
13175     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13176
13177   // set default position for snapshot buttons to stop/pause/play buttons
13178   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13179     if ((*game_buttons_xy[i].dst).x == -1 &&
13180         (*game_buttons_xy[i].dst).y == -1)
13181       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13182
13183   // --------------------------------------------------------------------------
13184   // dynamic viewports (including playfield margins, borders and alignments)
13185   // --------------------------------------------------------------------------
13186
13187   // dynamic viewports currently only supported for landscape mode
13188   int display_width  = MAX(video.display_width, video.display_height);
13189   int display_height = MIN(video.display_width, video.display_height);
13190
13191   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13192   {
13193     struct RectWithBorder *vp_window    = &viewport.window[i];
13194     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13195     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13196     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13197     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13198     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13199     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13200     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13201
13202     // adjust window size if min/max width/height is specified
13203
13204     if (vp_window->min_width != -1)
13205     {
13206       int window_width = display_width;
13207
13208       // when using static window height, use aspect ratio of display
13209       if (vp_window->min_height == -1)
13210         window_width = vp_window->height * display_width / display_height;
13211
13212       vp_window->width = MAX(vp_window->min_width, window_width);
13213     }
13214
13215     if (vp_window->min_height != -1)
13216     {
13217       int window_height = display_height;
13218
13219       // when using static window width, use aspect ratio of display
13220       if (vp_window->min_width == -1)
13221         window_height = vp_window->width * display_height / display_width;
13222
13223       vp_window->height = MAX(vp_window->min_height, window_height);
13224     }
13225
13226     if (vp_window->max_width != -1)
13227       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13228
13229     if (vp_window->max_height != -1)
13230       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13231
13232     int playfield_width  = vp_window->width;
13233     int playfield_height = vp_window->height;
13234
13235     // adjust playfield size and position according to specified margins
13236
13237     playfield_width  -= vp_playfield->margin_left;
13238     playfield_width  -= vp_playfield->margin_right;
13239
13240     playfield_height -= vp_playfield->margin_top;
13241     playfield_height -= vp_playfield->margin_bottom;
13242
13243     // adjust playfield size if min/max width/height is specified
13244
13245     if (vp_playfield->min_width != -1)
13246       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13247
13248     if (vp_playfield->min_height != -1)
13249       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13250
13251     if (vp_playfield->max_width != -1)
13252       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13253
13254     if (vp_playfield->max_height != -1)
13255       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13256
13257     // adjust playfield position according to specified alignment
13258
13259     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13260       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13261     else if (vp_playfield->align == ALIGN_CENTER)
13262       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13263     else if (vp_playfield->align == ALIGN_RIGHT)
13264       vp_playfield->x += playfield_width - vp_playfield->width;
13265
13266     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13267       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13268     else if (vp_playfield->valign == VALIGN_MIDDLE)
13269       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13270     else if (vp_playfield->valign == VALIGN_BOTTOM)
13271       vp_playfield->y += playfield_height - vp_playfield->height;
13272
13273     vp_playfield->x += vp_playfield->margin_left;
13274     vp_playfield->y += vp_playfield->margin_top;
13275
13276     // adjust individual playfield borders if only default border is specified
13277
13278     if (vp_playfield->border_left == -1)
13279       vp_playfield->border_left = vp_playfield->border_size;
13280     if (vp_playfield->border_right == -1)
13281       vp_playfield->border_right = vp_playfield->border_size;
13282     if (vp_playfield->border_top == -1)
13283       vp_playfield->border_top = vp_playfield->border_size;
13284     if (vp_playfield->border_bottom == -1)
13285       vp_playfield->border_bottom = vp_playfield->border_size;
13286
13287     // set dynamic playfield borders if borders are specified as undefined
13288     // (but only if window size was dynamic and playfield size was static)
13289
13290     if (dynamic_window_width && !dynamic_playfield_width)
13291     {
13292       if (vp_playfield->border_left == -1)
13293       {
13294         vp_playfield->border_left = (vp_playfield->x -
13295                                      vp_playfield->margin_left);
13296         vp_playfield->x     -= vp_playfield->border_left;
13297         vp_playfield->width += vp_playfield->border_left;
13298       }
13299
13300       if (vp_playfield->border_right == -1)
13301       {
13302         vp_playfield->border_right = (vp_window->width -
13303                                       vp_playfield->x -
13304                                       vp_playfield->width -
13305                                       vp_playfield->margin_right);
13306         vp_playfield->width += vp_playfield->border_right;
13307       }
13308     }
13309
13310     if (dynamic_window_height && !dynamic_playfield_height)
13311     {
13312       if (vp_playfield->border_top == -1)
13313       {
13314         vp_playfield->border_top = (vp_playfield->y -
13315                                     vp_playfield->margin_top);
13316         vp_playfield->y      -= vp_playfield->border_top;
13317         vp_playfield->height += vp_playfield->border_top;
13318       }
13319
13320       if (vp_playfield->border_bottom == -1)
13321       {
13322         vp_playfield->border_bottom = (vp_window->height -
13323                                        vp_playfield->y -
13324                                        vp_playfield->height -
13325                                        vp_playfield->margin_bottom);
13326         vp_playfield->height += vp_playfield->border_bottom;
13327       }
13328     }
13329
13330     // adjust playfield size to be a multiple of a defined alignment tile size
13331
13332     int align_size = vp_playfield->align_size;
13333     int playfield_xtiles = vp_playfield->width  / align_size;
13334     int playfield_ytiles = vp_playfield->height / align_size;
13335     int playfield_width_corrected  = playfield_xtiles * align_size;
13336     int playfield_height_corrected = playfield_ytiles * align_size;
13337     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13338                                  i == GFX_SPECIAL_ARG_EDITOR);
13339
13340     if (is_playfield_mode &&
13341         dynamic_playfield_width &&
13342         vp_playfield->width != playfield_width_corrected)
13343     {
13344       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13345
13346       vp_playfield->width = playfield_width_corrected;
13347
13348       if (vp_playfield->align == ALIGN_LEFT)
13349       {
13350         vp_playfield->border_left += playfield_xdiff;
13351       }
13352       else if (vp_playfield->align == ALIGN_RIGHT)
13353       {
13354         vp_playfield->border_right += playfield_xdiff;
13355       }
13356       else if (vp_playfield->align == ALIGN_CENTER)
13357       {
13358         int border_left_diff  = playfield_xdiff / 2;
13359         int border_right_diff = playfield_xdiff - border_left_diff;
13360
13361         vp_playfield->border_left  += border_left_diff;
13362         vp_playfield->border_right += border_right_diff;
13363       }
13364     }
13365
13366     if (is_playfield_mode &&
13367         dynamic_playfield_height &&
13368         vp_playfield->height != playfield_height_corrected)
13369     {
13370       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13371
13372       vp_playfield->height = playfield_height_corrected;
13373
13374       if (vp_playfield->valign == VALIGN_TOP)
13375       {
13376         vp_playfield->border_top += playfield_ydiff;
13377       }
13378       else if (vp_playfield->align == VALIGN_BOTTOM)
13379       {
13380         vp_playfield->border_right += playfield_ydiff;
13381       }
13382       else if (vp_playfield->align == VALIGN_MIDDLE)
13383       {
13384         int border_top_diff    = playfield_ydiff / 2;
13385         int border_bottom_diff = playfield_ydiff - border_top_diff;
13386
13387         vp_playfield->border_top    += border_top_diff;
13388         vp_playfield->border_bottom += border_bottom_diff;
13389       }
13390     }
13391
13392     // adjust door positions according to specified alignment
13393
13394     for (j = 0; j < 2; j++)
13395     {
13396       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13397
13398       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13399         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13400       else if (vp_door->align == ALIGN_CENTER)
13401         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13402       else if (vp_door->align == ALIGN_RIGHT)
13403         vp_door->x += vp_window->width - vp_door->width;
13404
13405       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13406         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13407       else if (vp_door->valign == VALIGN_MIDDLE)
13408         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13409       else if (vp_door->valign == VALIGN_BOTTOM)
13410         vp_door->y += vp_window->height - vp_door->height;
13411     }
13412   }
13413 }
13414
13415 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13416 {
13417   static struct
13418   {
13419     struct XYTileSize *dst, *src;
13420     int graphic;
13421   }
13422   editor_buttons_xy[] =
13423   {
13424     {
13425       &editor.button.element_left,      &editor.palette.element_left,
13426       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13427     },
13428     {
13429       &editor.button.element_middle,    &editor.palette.element_middle,
13430       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13431     },
13432     {
13433       &editor.button.element_right,     &editor.palette.element_right,
13434       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13435     },
13436
13437     { NULL,                     NULL                    }
13438   };
13439   int i;
13440
13441   // set default position for element buttons to element graphics
13442   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13443   {
13444     if ((*editor_buttons_xy[i].dst).x == -1 &&
13445         (*editor_buttons_xy[i].dst).y == -1)
13446     {
13447       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13448
13449       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13450
13451       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13452     }
13453   }
13454
13455   // adjust editor palette rows and columns if specified to be dynamic
13456
13457   if (editor.palette.cols == -1)
13458   {
13459     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13460     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13461     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13462
13463     editor.palette.cols = (vp_width - sc_width) / bt_width;
13464
13465     if (editor.palette.x == -1)
13466     {
13467       int palette_width = editor.palette.cols * bt_width + sc_width;
13468
13469       editor.palette.x = (vp_width - palette_width) / 2;
13470     }
13471   }
13472
13473   if (editor.palette.rows == -1)
13474   {
13475     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13476     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13477     int tx_height = getFontHeight(FONT_TEXT_2);
13478
13479     editor.palette.rows = (vp_height - tx_height) / bt_height;
13480
13481     if (editor.palette.y == -1)
13482     {
13483       int palette_height = editor.palette.rows * bt_height + tx_height;
13484
13485       editor.palette.y = (vp_height - palette_height) / 2;
13486     }
13487   }
13488 }
13489
13490 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13491                                                       boolean initialize)
13492 {
13493   // special case: check if network and preview player positions are redefined,
13494   // to compare this later against the main menu level preview being redefined
13495   struct TokenIntPtrInfo menu_config_players[] =
13496   {
13497     { "main.network_players.x", &menu.main.network_players.redefined    },
13498     { "main.network_players.y", &menu.main.network_players.redefined    },
13499     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13500     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13501     { "preview.x",              &preview.redefined                      },
13502     { "preview.y",              &preview.redefined                      }
13503   };
13504   int i;
13505
13506   if (initialize)
13507   {
13508     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13509       *menu_config_players[i].value = FALSE;
13510   }
13511   else
13512   {
13513     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13514       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13515         *menu_config_players[i].value = TRUE;
13516   }
13517 }
13518
13519 static void InitMenuDesignSettings_PreviewPlayers(void)
13520 {
13521   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13522 }
13523
13524 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13525 {
13526   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13527 }
13528
13529 static void LoadMenuDesignSettingsFromFilename(char *filename)
13530 {
13531   static struct TitleFadingInfo tfi;
13532   static struct TitleMessageInfo tmi;
13533   static struct TokenInfo title_tokens[] =
13534   {
13535     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13536     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13537     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13538     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13539     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13540
13541     { -1,               NULL,                   NULL                    }
13542   };
13543   static struct TokenInfo titlemessage_tokens[] =
13544   {
13545     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13546     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13547     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13548     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13549     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13550     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13551     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13552     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13553     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13554     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13555     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13556     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13557     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13558     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13559     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13560     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13561     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13562     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13563
13564     { -1,               NULL,                   NULL                    }
13565   };
13566   static struct
13567   {
13568     struct TitleFadingInfo *info;
13569     char *text;
13570   }
13571   title_info[] =
13572   {
13573     // initialize first titles from "enter screen" definitions, if defined
13574     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13575     { &title_first_default,             "menu.enter_screen.TITLE"       },
13576
13577     // initialize title screens from "next screen" definitions, if defined
13578     { &title_initial_default,           "menu.next_screen.TITLE"        },
13579     { &title_default,                   "menu.next_screen.TITLE"        },
13580
13581     { NULL,                             NULL                            }
13582   };
13583   static struct
13584   {
13585     struct TitleMessageInfo *array;
13586     char *text;
13587   }
13588   titlemessage_arrays[] =
13589   {
13590     // initialize first titles from "enter screen" definitions, if defined
13591     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13592     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13593     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13594     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13595
13596     // initialize titles from "next screen" definitions, if defined
13597     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13598     { titlescreen,                      "menu.next_screen.TITLE"        },
13599     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13600     { titlemessage,                     "menu.next_screen.TITLE"        },
13601
13602     // overwrite titles with title definitions, if defined
13603     { titlescreen_initial_first,        "[title_initial]"               },
13604     { titlescreen_first,                "[title]"                       },
13605     { titlemessage_initial_first,       "[title_initial]"               },
13606     { titlemessage_first,               "[title]"                       },
13607
13608     { titlescreen_initial,              "[title_initial]"               },
13609     { titlescreen,                      "[title]"                       },
13610     { titlemessage_initial,             "[title_initial]"               },
13611     { titlemessage,                     "[title]"                       },
13612
13613     // overwrite titles with title screen/message definitions, if defined
13614     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13615     { titlescreen_first,                "[titlescreen]"                 },
13616     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13617     { titlemessage_first,               "[titlemessage]"                },
13618
13619     { titlescreen_initial,              "[titlescreen_initial]"         },
13620     { titlescreen,                      "[titlescreen]"                 },
13621     { titlemessage_initial,             "[titlemessage_initial]"        },
13622     { titlemessage,                     "[titlemessage]"                },
13623
13624     { NULL,                             NULL                            }
13625   };
13626   SetupFileHash *setup_file_hash;
13627   int i, j, k;
13628
13629   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13630     return;
13631
13632   // the following initializes hierarchical values from dynamic configuration
13633
13634   // special case: initialize with default values that may be overwritten
13635   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13636   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13637   {
13638     struct TokenIntPtrInfo menu_config[] =
13639     {
13640       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13641       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13642       { "menu.list_size",       &menu.list_size[i]      }
13643     };
13644
13645     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13646     {
13647       char *token = menu_config[j].token;
13648       char *value = getHashEntry(setup_file_hash, token);
13649
13650       if (value != NULL)
13651         *menu_config[j].value = get_integer_from_string(value);
13652     }
13653   }
13654
13655   // special case: initialize with default values that may be overwritten
13656   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13657   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13658   {
13659     struct TokenIntPtrInfo menu_config[] =
13660     {
13661       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13662       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13663       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13664       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13665       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13666     };
13667
13668     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13669     {
13670       char *token = menu_config[j].token;
13671       char *value = getHashEntry(setup_file_hash, token);
13672
13673       if (value != NULL)
13674         *menu_config[j].value = get_integer_from_string(value);
13675     }
13676   }
13677
13678   // special case: initialize with default values that may be overwritten
13679   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13680   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13681   {
13682     struct TokenIntPtrInfo menu_config[] =
13683     {
13684       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13685       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13686     };
13687
13688     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13689     {
13690       char *token = menu_config[j].token;
13691       char *value = getHashEntry(setup_file_hash, token);
13692
13693       if (value != NULL)
13694         *menu_config[j].value = get_integer_from_string(value);
13695     }
13696   }
13697
13698   // special case: initialize with default values that may be overwritten
13699   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13700   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13701   {
13702     struct TokenIntPtrInfo menu_config[] =
13703     {
13704       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13705       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13706       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13707       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13708       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13709       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13710       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13711       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13712       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13713       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13714     };
13715
13716     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13717     {
13718       char *token = menu_config[j].token;
13719       char *value = getHashEntry(setup_file_hash, token);
13720
13721       if (value != NULL)
13722         *menu_config[j].value = get_integer_from_string(value);
13723     }
13724   }
13725
13726   // special case: initialize with default values that may be overwritten
13727   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13728   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13729   {
13730     struct TokenIntPtrInfo menu_config[] =
13731     {
13732       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13733       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13734       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13735       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13736       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13737       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13738       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13739       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13740       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13741     };
13742
13743     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13744     {
13745       char *token = menu_config[j].token;
13746       char *value = getHashEntry(setup_file_hash, token);
13747
13748       if (value != NULL)
13749         *menu_config[j].value = get_token_parameter_value(token, value);
13750     }
13751   }
13752
13753   // special case: initialize with default values that may be overwritten
13754   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13755   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13756   {
13757     struct
13758     {
13759       char *token_prefix;
13760       struct RectWithBorder *struct_ptr;
13761     }
13762     vp_struct[] =
13763     {
13764       { "viewport.window",      &viewport.window[i]     },
13765       { "viewport.playfield",   &viewport.playfield[i]  },
13766       { "viewport.door_1",      &viewport.door_1[i]     },
13767       { "viewport.door_2",      &viewport.door_2[i]     }
13768     };
13769
13770     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13771     {
13772       struct TokenIntPtrInfo vp_config[] =
13773       {
13774         { ".x",                 &vp_struct[j].struct_ptr->x             },
13775         { ".y",                 &vp_struct[j].struct_ptr->y             },
13776         { ".width",             &vp_struct[j].struct_ptr->width         },
13777         { ".height",            &vp_struct[j].struct_ptr->height        },
13778         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13779         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13780         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13781         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13782         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13783         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13784         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13785         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13786         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13787         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13788         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13789         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13790         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13791         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13792         { ".align",             &vp_struct[j].struct_ptr->align         },
13793         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13794       };
13795
13796       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13797       {
13798         char *token = getStringCat2(vp_struct[j].token_prefix,
13799                                     vp_config[k].token);
13800         char *value = getHashEntry(setup_file_hash, token);
13801
13802         if (value != NULL)
13803           *vp_config[k].value = get_token_parameter_value(token, value);
13804
13805         free(token);
13806       }
13807     }
13808   }
13809
13810   // special case: initialize with default values that may be overwritten
13811   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13812   for (i = 0; title_info[i].info != NULL; i++)
13813   {
13814     struct TitleFadingInfo *info = title_info[i].info;
13815     char *base_token = title_info[i].text;
13816
13817     for (j = 0; title_tokens[j].type != -1; j++)
13818     {
13819       char *token = getStringCat2(base_token, title_tokens[j].text);
13820       char *value = getHashEntry(setup_file_hash, token);
13821
13822       if (value != NULL)
13823       {
13824         int parameter_value = get_token_parameter_value(token, value);
13825
13826         tfi = *info;
13827
13828         *(int *)title_tokens[j].value = (int)parameter_value;
13829
13830         *info = tfi;
13831       }
13832
13833       free(token);
13834     }
13835   }
13836
13837   // special case: initialize with default values that may be overwritten
13838   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13839   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13840   {
13841     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13842     char *base_token = titlemessage_arrays[i].text;
13843
13844     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13845     {
13846       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13847       char *value = getHashEntry(setup_file_hash, token);
13848
13849       if (value != NULL)
13850       {
13851         int parameter_value = get_token_parameter_value(token, value);
13852
13853         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13854         {
13855           tmi = array[k];
13856
13857           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13858             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13859           else
13860             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13861
13862           array[k] = tmi;
13863         }
13864       }
13865
13866       free(token);
13867     }
13868   }
13869
13870   // read (and overwrite with) values that may be specified in config file
13871   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13872
13873   // special case: check if network and preview player positions are redefined
13874   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13875
13876   freeSetupFileHash(setup_file_hash);
13877 }
13878
13879 void LoadMenuDesignSettings(void)
13880 {
13881   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13882
13883   InitMenuDesignSettings_Static();
13884   InitMenuDesignSettings_SpecialPreProcessing();
13885   InitMenuDesignSettings_PreviewPlayers();
13886
13887   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13888   {
13889     // first look for special settings configured in level series config
13890     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13891
13892     if (fileExists(filename_base))
13893       LoadMenuDesignSettingsFromFilename(filename_base);
13894   }
13895
13896   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13897
13898   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13899     LoadMenuDesignSettingsFromFilename(filename_local);
13900
13901   InitMenuDesignSettings_SpecialPostProcessing();
13902 }
13903
13904 void LoadMenuDesignSettings_AfterGraphics(void)
13905 {
13906   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13907 }
13908
13909 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13910                                 boolean ignore_defaults)
13911 {
13912   int i;
13913
13914   for (i = 0; sound_config_vars[i].token != NULL; i++)
13915   {
13916     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13917
13918     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13919     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13920       continue;
13921
13922     if (value != NULL)
13923       *sound_config_vars[i].value =
13924         get_token_parameter_value(sound_config_vars[i].token, value);
13925   }
13926 }
13927
13928 void InitSoundSettings_Static(void)
13929 {
13930   // always start with reliable default values from static default config
13931   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13932 }
13933
13934 static void LoadSoundSettingsFromFilename(char *filename)
13935 {
13936   SetupFileHash *setup_file_hash;
13937
13938   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13939     return;
13940
13941   // read (and overwrite with) values that may be specified in config file
13942   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13943
13944   freeSetupFileHash(setup_file_hash);
13945 }
13946
13947 void LoadSoundSettings(void)
13948 {
13949   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13950
13951   InitSoundSettings_Static();
13952
13953   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13954   {
13955     // first look for special settings configured in level series config
13956     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13957
13958     if (fileExists(filename_base))
13959       LoadSoundSettingsFromFilename(filename_base);
13960   }
13961
13962   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13963
13964   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13965     LoadSoundSettingsFromFilename(filename_local);
13966 }
13967
13968 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13969 {
13970   char *filename = getEditorSetupFilename();
13971   SetupFileList *setup_file_list, *list;
13972   SetupFileHash *element_hash;
13973   int num_unknown_tokens = 0;
13974   int i;
13975
13976   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13977     return;
13978
13979   element_hash = newSetupFileHash();
13980
13981   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13982     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13983
13984   // determined size may be larger than needed (due to unknown elements)
13985   *num_elements = 0;
13986   for (list = setup_file_list; list != NULL; list = list->next)
13987     (*num_elements)++;
13988
13989   // add space for up to 3 more elements for padding that may be needed
13990   *num_elements += 3;
13991
13992   // free memory for old list of elements, if needed
13993   checked_free(*elements);
13994
13995   // allocate memory for new list of elements
13996   *elements = checked_malloc(*num_elements * sizeof(int));
13997
13998   *num_elements = 0;
13999   for (list = setup_file_list; list != NULL; list = list->next)
14000   {
14001     char *value = getHashEntry(element_hash, list->token);
14002
14003     if (value == NULL)          // try to find obsolete token mapping
14004     {
14005       char *mapped_token = get_mapped_token(list->token);
14006
14007       if (mapped_token != NULL)
14008       {
14009         value = getHashEntry(element_hash, mapped_token);
14010
14011         free(mapped_token);
14012       }
14013     }
14014
14015     if (value != NULL)
14016     {
14017       (*elements)[(*num_elements)++] = atoi(value);
14018     }
14019     else
14020     {
14021       if (num_unknown_tokens == 0)
14022       {
14023         Warn("---");
14024         Warn("unknown token(s) found in config file:");
14025         Warn("- config file: '%s'", filename);
14026
14027         num_unknown_tokens++;
14028       }
14029
14030       Warn("- token: '%s'", list->token);
14031     }
14032   }
14033
14034   if (num_unknown_tokens > 0)
14035     Warn("---");
14036
14037   while (*num_elements % 4)     // pad with empty elements, if needed
14038     (*elements)[(*num_elements)++] = EL_EMPTY;
14039
14040   freeSetupFileList(setup_file_list);
14041   freeSetupFileHash(element_hash);
14042
14043 #if 0
14044   for (i = 0; i < *num_elements; i++)
14045     Debug("editor", "element '%s' [%d]\n",
14046           element_info[(*elements)[i]].token_name, (*elements)[i]);
14047 #endif
14048 }
14049
14050 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
14051                                                      boolean is_sound)
14052 {
14053   SetupFileHash *setup_file_hash = NULL;
14054   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
14055   char *filename_music, *filename_prefix, *filename_info;
14056   struct
14057   {
14058     char *token;
14059     char **value_ptr;
14060   }
14061   token_to_value_ptr[] =
14062   {
14063     { "title_header",   &tmp_music_file_info.title_header       },
14064     { "artist_header",  &tmp_music_file_info.artist_header      },
14065     { "album_header",   &tmp_music_file_info.album_header       },
14066     { "year_header",    &tmp_music_file_info.year_header        },
14067     { "played_header",  &tmp_music_file_info.played_header      },
14068
14069     { "title",          &tmp_music_file_info.title              },
14070     { "artist",         &tmp_music_file_info.artist             },
14071     { "album",          &tmp_music_file_info.album              },
14072     { "year",           &tmp_music_file_info.year               },
14073     { "played",         &tmp_music_file_info.played             },
14074
14075     { NULL,             NULL                                    },
14076   };
14077   int i;
14078
14079   filename_music = (is_sound ? getCustomSoundFilename(basename) :
14080                     getCustomMusicFilename(basename));
14081
14082   if (filename_music == NULL)
14083     return NULL;
14084
14085   // ---------- try to replace file extension ----------
14086
14087   filename_prefix = getStringCopy(filename_music);
14088   if (strrchr(filename_prefix, '.') != NULL)
14089     *strrchr(filename_prefix, '.') = '\0';
14090   filename_info = getStringCat2(filename_prefix, ".txt");
14091
14092   if (fileExists(filename_info))
14093     setup_file_hash = loadSetupFileHash(filename_info);
14094
14095   free(filename_prefix);
14096   free(filename_info);
14097
14098   if (setup_file_hash == NULL)
14099   {
14100     // ---------- try to add file extension ----------
14101
14102     filename_prefix = getStringCopy(filename_music);
14103     filename_info = getStringCat2(filename_prefix, ".txt");
14104
14105     if (fileExists(filename_info))
14106       setup_file_hash = loadSetupFileHash(filename_info);
14107
14108     free(filename_prefix);
14109     free(filename_info);
14110   }
14111
14112   if (setup_file_hash == NULL)
14113     return NULL;
14114
14115   // ---------- music file info found ----------
14116
14117   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
14118
14119   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
14120   {
14121     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
14122
14123     *token_to_value_ptr[i].value_ptr =
14124       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
14125   }
14126
14127   tmp_music_file_info.basename = getStringCopy(basename);
14128   tmp_music_file_info.music = music;
14129   tmp_music_file_info.is_sound = is_sound;
14130
14131   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
14132   *new_music_file_info = tmp_music_file_info;
14133
14134   return new_music_file_info;
14135 }
14136
14137 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
14138 {
14139   return get_music_file_info_ext(basename, music, FALSE);
14140 }
14141
14142 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
14143 {
14144   return get_music_file_info_ext(basename, sound, TRUE);
14145 }
14146
14147 static boolean music_info_listed_ext(struct MusicFileInfo *list,
14148                                      char *basename, boolean is_sound)
14149 {
14150   for (; list != NULL; list = list->next)
14151     if (list->is_sound == is_sound && strEqual(list->basename, basename))
14152       return TRUE;
14153
14154   return FALSE;
14155 }
14156
14157 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14158 {
14159   return music_info_listed_ext(list, basename, FALSE);
14160 }
14161
14162 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14163 {
14164   return music_info_listed_ext(list, basename, TRUE);
14165 }
14166
14167 void LoadMusicInfo(void)
14168 {
14169   int num_music_noconf = getMusicListSize_NoConf();
14170   int num_music = getMusicListSize();
14171   int num_sounds = getSoundListSize();
14172   struct FileInfo *music, *sound;
14173   struct MusicFileInfo *next, **new;
14174
14175   int i;
14176
14177   while (music_file_info != NULL)
14178   {
14179     next = music_file_info->next;
14180
14181     checked_free(music_file_info->basename);
14182
14183     checked_free(music_file_info->title_header);
14184     checked_free(music_file_info->artist_header);
14185     checked_free(music_file_info->album_header);
14186     checked_free(music_file_info->year_header);
14187     checked_free(music_file_info->played_header);
14188
14189     checked_free(music_file_info->title);
14190     checked_free(music_file_info->artist);
14191     checked_free(music_file_info->album);
14192     checked_free(music_file_info->year);
14193     checked_free(music_file_info->played);
14194
14195     free(music_file_info);
14196
14197     music_file_info = next;
14198   }
14199
14200   new = &music_file_info;
14201
14202   // get (configured or unconfigured) music file info for all levels
14203   for (i = leveldir_current->first_level;
14204        i <= leveldir_current->last_level; i++)
14205   {
14206     int music_nr;
14207
14208     if (levelset.music[i] != MUS_UNDEFINED)
14209     {
14210       // get music file info for configured level music
14211       music_nr = levelset.music[i];
14212     }
14213     else if (num_music_noconf > 0)
14214     {
14215       // get music file info for unconfigured level music
14216       int level_pos = i - leveldir_current->first_level;
14217
14218       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14219     }
14220     else
14221     {
14222       continue;
14223     }
14224
14225     char *basename = getMusicInfoEntryFilename(music_nr);
14226
14227     if (basename == NULL)
14228       continue;
14229
14230     if (!music_info_listed(music_file_info, basename))
14231     {
14232       *new = get_music_file_info(basename, music_nr);
14233
14234       if (*new != NULL)
14235         new = &(*new)->next;
14236     }
14237   }
14238
14239   // get music file info for all remaining configured music files
14240   for (i = 0; i < num_music; i++)
14241   {
14242     music = getMusicListEntry(i);
14243
14244     if (music->filename == NULL)
14245       continue;
14246
14247     if (strEqual(music->filename, UNDEFINED_FILENAME))
14248       continue;
14249
14250     // a configured file may be not recognized as music
14251     if (!FileIsMusic(music->filename))
14252       continue;
14253
14254     if (!music_info_listed(music_file_info, music->filename))
14255     {
14256       *new = get_music_file_info(music->filename, i);
14257
14258       if (*new != NULL)
14259         new = &(*new)->next;
14260     }
14261   }
14262
14263   // get sound file info for all configured sound files
14264   for (i = 0; i < num_sounds; i++)
14265   {
14266     sound = getSoundListEntry(i);
14267
14268     if (sound->filename == NULL)
14269       continue;
14270
14271     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14272       continue;
14273
14274     // a configured file may be not recognized as sound
14275     if (!FileIsSound(sound->filename))
14276       continue;
14277
14278     if (!sound_info_listed(music_file_info, sound->filename))
14279     {
14280       *new = get_sound_file_info(sound->filename, i);
14281       if (*new != NULL)
14282         new = &(*new)->next;
14283     }
14284   }
14285
14286   // add pointers to previous list nodes
14287
14288   struct MusicFileInfo *node = music_file_info;
14289
14290   while (node != NULL)
14291   {
14292     if (node->next)
14293       node->next->prev = node;
14294
14295     node = node->next;
14296   }
14297 }
14298
14299 static void add_helpanim_entry(int element, int action, int direction,
14300                                int delay, int *num_list_entries)
14301 {
14302   struct HelpAnimInfo *new_list_entry;
14303   (*num_list_entries)++;
14304
14305   helpanim_info =
14306     checked_realloc(helpanim_info,
14307                     *num_list_entries * sizeof(struct HelpAnimInfo));
14308   new_list_entry = &helpanim_info[*num_list_entries - 1];
14309
14310   new_list_entry->element = element;
14311   new_list_entry->action = action;
14312   new_list_entry->direction = direction;
14313   new_list_entry->delay = delay;
14314 }
14315
14316 static void print_unknown_token(char *filename, char *token, int token_nr)
14317 {
14318   if (token_nr == 0)
14319   {
14320     Warn("---");
14321     Warn("unknown token(s) found in config file:");
14322     Warn("- config file: '%s'", filename);
14323   }
14324
14325   Warn("- token: '%s'", token);
14326 }
14327
14328 static void print_unknown_token_end(int token_nr)
14329 {
14330   if (token_nr > 0)
14331     Warn("---");
14332 }
14333
14334 void LoadHelpAnimInfo(void)
14335 {
14336   char *filename = getHelpAnimFilename();
14337   SetupFileList *setup_file_list = NULL, *list;
14338   SetupFileHash *element_hash, *action_hash, *direction_hash;
14339   int num_list_entries = 0;
14340   int num_unknown_tokens = 0;
14341   int i;
14342
14343   if (fileExists(filename))
14344     setup_file_list = loadSetupFileList(filename);
14345
14346   if (setup_file_list == NULL)
14347   {
14348     // use reliable default values from static configuration
14349     SetupFileList *insert_ptr;
14350
14351     insert_ptr = setup_file_list =
14352       newSetupFileList(helpanim_config[0].token,
14353                        helpanim_config[0].value);
14354
14355     for (i = 1; helpanim_config[i].token; i++)
14356       insert_ptr = addListEntry(insert_ptr,
14357                                 helpanim_config[i].token,
14358                                 helpanim_config[i].value);
14359   }
14360
14361   element_hash   = newSetupFileHash();
14362   action_hash    = newSetupFileHash();
14363   direction_hash = newSetupFileHash();
14364
14365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14366     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14367
14368   for (i = 0; i < NUM_ACTIONS; i++)
14369     setHashEntry(action_hash, element_action_info[i].suffix,
14370                  i_to_a(element_action_info[i].value));
14371
14372   // do not store direction index (bit) here, but direction value!
14373   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14374     setHashEntry(direction_hash, element_direction_info[i].suffix,
14375                  i_to_a(1 << element_direction_info[i].value));
14376
14377   for (list = setup_file_list; list != NULL; list = list->next)
14378   {
14379     char *element_token, *action_token, *direction_token;
14380     char *element_value, *action_value, *direction_value;
14381     int delay = atoi(list->value);
14382
14383     if (strEqual(list->token, "end"))
14384     {
14385       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14386
14387       continue;
14388     }
14389
14390     /* first try to break element into element/action/direction parts;
14391        if this does not work, also accept combined "element[.act][.dir]"
14392        elements (like "dynamite.active"), which are unique elements */
14393
14394     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14395     {
14396       element_value = getHashEntry(element_hash, list->token);
14397       if (element_value != NULL)        // element found
14398         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14399                            &num_list_entries);
14400       else
14401       {
14402         // no further suffixes found -- this is not an element
14403         print_unknown_token(filename, list->token, num_unknown_tokens++);
14404       }
14405
14406       continue;
14407     }
14408
14409     // token has format "<prefix>.<something>"
14410
14411     action_token = strchr(list->token, '.');    // suffix may be action ...
14412     direction_token = action_token;             // ... or direction
14413
14414     element_token = getStringCopy(list->token);
14415     *strchr(element_token, '.') = '\0';
14416
14417     element_value = getHashEntry(element_hash, element_token);
14418
14419     if (element_value == NULL)          // this is no element
14420     {
14421       element_value = getHashEntry(element_hash, list->token);
14422       if (element_value != NULL)        // combined element found
14423         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14424                            &num_list_entries);
14425       else
14426         print_unknown_token(filename, list->token, num_unknown_tokens++);
14427
14428       free(element_token);
14429
14430       continue;
14431     }
14432
14433     action_value = getHashEntry(action_hash, action_token);
14434
14435     if (action_value != NULL)           // action found
14436     {
14437       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14438                     &num_list_entries);
14439
14440       free(element_token);
14441
14442       continue;
14443     }
14444
14445     direction_value = getHashEntry(direction_hash, direction_token);
14446
14447     if (direction_value != NULL)        // direction found
14448     {
14449       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14450                          &num_list_entries);
14451
14452       free(element_token);
14453
14454       continue;
14455     }
14456
14457     if (strchr(action_token + 1, '.') == NULL)
14458     {
14459       // no further suffixes found -- this is not an action nor direction
14460
14461       element_value = getHashEntry(element_hash, list->token);
14462       if (element_value != NULL)        // combined element found
14463         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14464                            &num_list_entries);
14465       else
14466         print_unknown_token(filename, list->token, num_unknown_tokens++);
14467
14468       free(element_token);
14469
14470       continue;
14471     }
14472
14473     // token has format "<prefix>.<suffix>.<something>"
14474
14475     direction_token = strchr(action_token + 1, '.');
14476
14477     action_token = getStringCopy(action_token);
14478     *strchr(action_token + 1, '.') = '\0';
14479
14480     action_value = getHashEntry(action_hash, action_token);
14481
14482     if (action_value == NULL)           // this is no action
14483     {
14484       element_value = getHashEntry(element_hash, list->token);
14485       if (element_value != NULL)        // combined element found
14486         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14487                            &num_list_entries);
14488       else
14489         print_unknown_token(filename, list->token, num_unknown_tokens++);
14490
14491       free(element_token);
14492       free(action_token);
14493
14494       continue;
14495     }
14496
14497     direction_value = getHashEntry(direction_hash, direction_token);
14498
14499     if (direction_value != NULL)        // direction found
14500     {
14501       add_helpanim_entry(atoi(element_value), atoi(action_value),
14502                          atoi(direction_value), delay, &num_list_entries);
14503
14504       free(element_token);
14505       free(action_token);
14506
14507       continue;
14508     }
14509
14510     // this is no direction
14511
14512     element_value = getHashEntry(element_hash, list->token);
14513     if (element_value != NULL)          // combined element found
14514       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14515                          &num_list_entries);
14516     else
14517       print_unknown_token(filename, list->token, num_unknown_tokens++);
14518
14519     free(element_token);
14520     free(action_token);
14521   }
14522
14523   print_unknown_token_end(num_unknown_tokens);
14524
14525   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14526   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14527
14528   freeSetupFileList(setup_file_list);
14529   freeSetupFileHash(element_hash);
14530   freeSetupFileHash(action_hash);
14531   freeSetupFileHash(direction_hash);
14532
14533 #if 0
14534   for (i = 0; i < num_list_entries; i++)
14535     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14536           EL_NAME(helpanim_info[i].element),
14537           helpanim_info[i].element,
14538           helpanim_info[i].action,
14539           helpanim_info[i].direction,
14540           helpanim_info[i].delay);
14541 #endif
14542 }
14543
14544 void LoadHelpTextInfo(void)
14545 {
14546   char *filename = getHelpTextFilename();
14547   int i;
14548
14549   if (helptext_info != NULL)
14550   {
14551     freeSetupFileHash(helptext_info);
14552     helptext_info = NULL;
14553   }
14554
14555   if (fileExists(filename))
14556     helptext_info = loadSetupFileHash(filename);
14557
14558   if (helptext_info == NULL)
14559   {
14560     // use reliable default values from static configuration
14561     helptext_info = newSetupFileHash();
14562
14563     for (i = 0; helptext_config[i].token; i++)
14564       setHashEntry(helptext_info,
14565                    helptext_config[i].token,
14566                    helptext_config[i].value);
14567   }
14568
14569 #if 0
14570   BEGIN_HASH_ITERATION(helptext_info, itr)
14571   {
14572     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14573           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14574   }
14575   END_HASH_ITERATION(hash, itr)
14576 #endif
14577 }
14578
14579
14580 // ----------------------------------------------------------------------------
14581 // convert levels
14582 // ----------------------------------------------------------------------------
14583
14584 #define MAX_NUM_CONVERT_LEVELS          1000
14585
14586 void ConvertLevels(void)
14587 {
14588   static LevelDirTree *convert_leveldir = NULL;
14589   static int convert_level_nr = -1;
14590   static int num_levels_handled = 0;
14591   static int num_levels_converted = 0;
14592   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14593   int i;
14594
14595   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14596                                                global.convert_leveldir);
14597
14598   if (convert_leveldir == NULL)
14599     Fail("no such level identifier: '%s'", global.convert_leveldir);
14600
14601   leveldir_current = convert_leveldir;
14602
14603   if (global.convert_level_nr != -1)
14604   {
14605     convert_leveldir->first_level = global.convert_level_nr;
14606     convert_leveldir->last_level  = global.convert_level_nr;
14607   }
14608
14609   convert_level_nr = convert_leveldir->first_level;
14610
14611   PrintLine("=", 79);
14612   Print("Converting levels\n");
14613   PrintLine("-", 79);
14614   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14615   Print("Level series name:       '%s'\n", convert_leveldir->name);
14616   Print("Level series author:     '%s'\n", convert_leveldir->author);
14617   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14618   PrintLine("=", 79);
14619   Print("\n");
14620
14621   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14622     levels_failed[i] = FALSE;
14623
14624   while (convert_level_nr <= convert_leveldir->last_level)
14625   {
14626     char *level_filename;
14627     boolean new_level;
14628
14629     level_nr = convert_level_nr++;
14630
14631     Print("Level %03d: ", level_nr);
14632
14633     LoadLevel(level_nr);
14634     if (level.no_level_file || level.no_valid_file)
14635     {
14636       Print("(no level)\n");
14637       continue;
14638     }
14639
14640     Print("converting level ... ");
14641
14642 #if 0
14643     // special case: conversion of some EMC levels as requested by ACME
14644     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14645 #endif
14646
14647     level_filename = getDefaultLevelFilename(level_nr);
14648     new_level = !fileExists(level_filename);
14649
14650     if (new_level)
14651     {
14652       SaveLevel(level_nr);
14653
14654       num_levels_converted++;
14655
14656       Print("converted.\n");
14657     }
14658     else
14659     {
14660       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14661         levels_failed[level_nr] = TRUE;
14662
14663       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14664     }
14665
14666     num_levels_handled++;
14667   }
14668
14669   Print("\n");
14670   PrintLine("=", 79);
14671   Print("Number of levels handled: %d\n", num_levels_handled);
14672   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14673          (num_levels_handled ?
14674           num_levels_converted * 100 / num_levels_handled : 0));
14675   PrintLine("-", 79);
14676   Print("Summary (for automatic parsing by scripts):\n");
14677   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14678          convert_leveldir->identifier, num_levels_converted,
14679          num_levels_handled,
14680          (num_levels_handled ?
14681           num_levels_converted * 100 / num_levels_handled : 0));
14682
14683   if (num_levels_handled != num_levels_converted)
14684   {
14685     Print(", FAILED:");
14686     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14687       if (levels_failed[i])
14688         Print(" %03d", i);
14689   }
14690
14691   Print("\n");
14692   PrintLine("=", 79);
14693
14694   CloseAllAndExit(0);
14695 }
14696
14697
14698 // ----------------------------------------------------------------------------
14699 // create and save images for use in level sketches (raw BMP format)
14700 // ----------------------------------------------------------------------------
14701
14702 void CreateLevelSketchImages(void)
14703 {
14704   Bitmap *bitmap1;
14705   Bitmap *bitmap2;
14706   int i;
14707
14708   InitElementPropertiesGfxElement();
14709
14710   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14711   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14712
14713   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14714   {
14715     int element = getMappedElement(i);
14716     char basename1[16];
14717     char basename2[16];
14718     char *filename1;
14719     char *filename2;
14720
14721     sprintf(basename1, "%04d.bmp", i);
14722     sprintf(basename2, "%04ds.bmp", i);
14723
14724     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14725     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14726
14727     DrawSizedElement(0, 0, element, TILESIZE);
14728     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14729
14730     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14731       Fail("cannot save level sketch image file '%s'", filename1);
14732
14733     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14734     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14735
14736     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14737       Fail("cannot save level sketch image file '%s'", filename2);
14738
14739     free(filename1);
14740     free(filename2);
14741
14742     // create corresponding SQL statements (for normal and small images)
14743     if (i < 1000)
14744     {
14745       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14746       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14747     }
14748
14749     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14750     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14751
14752     // optional: create content for forum level sketch demonstration post
14753     if (options.debug)
14754       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14755   }
14756
14757   FreeBitmap(bitmap1);
14758   FreeBitmap(bitmap2);
14759
14760   if (options.debug)
14761     fprintf(stderr, "\n");
14762
14763   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14764
14765   CloseAllAndExit(0);
14766 }
14767
14768
14769 // ----------------------------------------------------------------------------
14770 // create and save images for element collecting animations (raw BMP format)
14771 // ----------------------------------------------------------------------------
14772
14773 static boolean createCollectImage(int element)
14774 {
14775   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14776 }
14777
14778 void CreateCollectElementImages(void)
14779 {
14780   int i, j;
14781   int num_steps = 8;
14782   int anim_frames = num_steps - 1;
14783   int tile_size = TILESIZE;
14784   int anim_width  = tile_size * anim_frames;
14785   int anim_height = tile_size;
14786   int num_collect_images = 0;
14787   int pos_collect_images = 0;
14788
14789   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14790     if (createCollectImage(i))
14791       num_collect_images++;
14792
14793   Info("Creating %d element collecting animation images ...",
14794        num_collect_images);
14795
14796   int dst_width  = anim_width * 2;
14797   int dst_height = anim_height * num_collect_images / 2;
14798   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14799   char *basename_bmp = "RocksCollect.bmp";
14800   char *basename_png = "RocksCollect.png";
14801   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14802   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14803   int len_filename_bmp = strlen(filename_bmp);
14804   int len_filename_png = strlen(filename_png);
14805   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14806   char cmd_convert[max_command_len];
14807
14808   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14809            filename_bmp,
14810            filename_png);
14811
14812   // force using RGBA surface for destination bitmap
14813   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14814                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14815
14816   dst_bitmap->surface =
14817     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14818
14819   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14820   {
14821     if (!createCollectImage(i))
14822       continue;
14823
14824     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14825     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14826     int graphic = el2img(i);
14827     char *token_name = element_info[i].token_name;
14828     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14829     Bitmap *src_bitmap;
14830     int src_x, src_y;
14831
14832     Info("- creating collecting image for '%s' ...", token_name);
14833
14834     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14835
14836     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14837                tile_size, tile_size, 0, 0);
14838
14839     // force using RGBA surface for temporary bitmap (using transparent black)
14840     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14841                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14842
14843     tmp_bitmap->surface =
14844       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14845
14846     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14847
14848     for (j = 0; j < anim_frames; j++)
14849     {
14850       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14851       int frame_size = frame_size_final * num_steps;
14852       int offset = (tile_size - frame_size_final) / 2;
14853       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14854
14855       while (frame_size > frame_size_final)
14856       {
14857         frame_size /= 2;
14858
14859         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14860
14861         FreeBitmap(frame_bitmap);
14862
14863         frame_bitmap = half_bitmap;
14864       }
14865
14866       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14867                        frame_size_final, frame_size_final,
14868                        dst_x + j * tile_size + offset, dst_y + offset);
14869
14870       FreeBitmap(frame_bitmap);
14871     }
14872
14873     tmp_bitmap->surface_masked = NULL;
14874
14875     FreeBitmap(tmp_bitmap);
14876
14877     pos_collect_images++;
14878   }
14879
14880   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14881     Fail("cannot save element collecting image file '%s'", filename_bmp);
14882
14883   FreeBitmap(dst_bitmap);
14884
14885   Info("Converting image file from BMP to PNG ...");
14886
14887   if (system(cmd_convert) != 0)
14888     Fail("converting image file failed");
14889
14890   unlink(filename_bmp);
14891
14892   Info("Done.");
14893
14894   CloseAllAndExit(0);
14895 }
14896
14897
14898 // ----------------------------------------------------------------------------
14899 // create and save images for custom and group elements (raw BMP format)
14900 // ----------------------------------------------------------------------------
14901
14902 void CreateCustomElementImages(char *directory)
14903 {
14904   char *src_basename = "RocksCE-template.ilbm";
14905   char *dst_basename = "RocksCE.bmp";
14906   char *src_filename = getPath2(directory, src_basename);
14907   char *dst_filename = getPath2(directory, dst_basename);
14908   Bitmap *src_bitmap;
14909   Bitmap *bitmap;
14910   int yoffset_ce = 0;
14911   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14912   int i;
14913
14914   InitVideoDefaults();
14915
14916   ReCreateBitmap(&backbuffer, video.width, video.height);
14917
14918   src_bitmap = LoadImage(src_filename);
14919
14920   bitmap = CreateBitmap(TILEX * 16 * 2,
14921                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14922                         DEFAULT_DEPTH);
14923
14924   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14925   {
14926     int x = i % 16;
14927     int y = i / 16;
14928     int ii = i + 1;
14929     int j;
14930
14931     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14932                TILEX * x, TILEY * y + yoffset_ce);
14933
14934     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14935                TILEX, TILEY,
14936                TILEX * x + TILEX * 16,
14937                TILEY * y + yoffset_ce);
14938
14939     for (j = 2; j >= 0; j--)
14940     {
14941       int c = ii % 10;
14942
14943       BlitBitmap(src_bitmap, bitmap,
14944                  TILEX + c * 7, 0, 6, 10,
14945                  TILEX * x + 6 + j * 7,
14946                  TILEY * y + 11 + yoffset_ce);
14947
14948       BlitBitmap(src_bitmap, bitmap,
14949                  TILEX + c * 8, TILEY, 6, 10,
14950                  TILEX * 16 + TILEX * x + 6 + j * 8,
14951                  TILEY * y + 10 + yoffset_ce);
14952
14953       ii /= 10;
14954     }
14955   }
14956
14957   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14958   {
14959     int x = i % 16;
14960     int y = i / 16;
14961     int ii = i + 1;
14962     int j;
14963
14964     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14965                TILEX * x, TILEY * y + yoffset_ge);
14966
14967     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14968                TILEX, TILEY,
14969                TILEX * x + TILEX * 16,
14970                TILEY * y + yoffset_ge);
14971
14972     for (j = 1; j >= 0; j--)
14973     {
14974       int c = ii % 10;
14975
14976       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14977                  TILEX * x + 6 + j * 10,
14978                  TILEY * y + 11 + yoffset_ge);
14979
14980       BlitBitmap(src_bitmap, bitmap,
14981                  TILEX + c * 8, TILEY + 12, 6, 10,
14982                  TILEX * 16 + TILEX * x + 10 + j * 8,
14983                  TILEY * y + 10 + yoffset_ge);
14984
14985       ii /= 10;
14986     }
14987   }
14988
14989   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14990     Fail("cannot save CE graphics file '%s'", dst_filename);
14991
14992   FreeBitmap(bitmap);
14993
14994   CloseAllAndExit(0);
14995 }