extended support for expanding wall settings in BD engine to level editor
[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_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
749     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
754     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
759     &li.bd_slime_eats_element_2,        EL_BD_ROCK
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
764     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
769     &li.bd_slime_eats_element_3,        EL_BD_NUT
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
774     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
775   },
776
777   {
778     EL_BD_ACID,                         -1,
779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
780     &li.bd_acid_eats_element,           EL_BD_SAND
781   },
782   {
783     EL_BD_ACID,                         -1,
784     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
785     &li.bd_acid_spread_rate,            3
786   },
787   {
788     EL_BD_ACID,                         -1,
789     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
790     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
791   },
792
793   {
794     EL_BD_BITER,                        -1,
795     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
796     &li.bd_biter_move_delay,            0
797   },
798   {
799     EL_BD_BITER,                        -1,
800     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
801     &li.bd_biter_eats_element,          EL_BD_DIAMOND
802   },
803
804   {
805     EL_BD_BLADDER,                      -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
808   },
809
810   {
811     EL_BD_EXPANDABLE_WALL_ANY,          -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.bd_change_expanding_wall,       FALSE
814   },
815   {
816     EL_BD_EXPANDABLE_WALL_ANY,          -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
818     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
819   },
820
821   {
822     EL_BD_REPLICATOR,                   -1,
823     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
824     &li.bd_replicators_active,          TRUE
825   },
826   {
827     EL_BD_REPLICATOR,                   -1,
828     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
829     &li.bd_replicator_create_delay,     4
830   },
831
832   {
833     EL_BD_CONVEYOR_LEFT,                -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
835     &li.bd_conveyor_belts_active,       TRUE
836   },
837   {
838     EL_BD_CONVEYOR_LEFT,                -1,
839     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
840     &li.bd_conveyor_belts_changed,      FALSE
841   },
842
843   {
844     EL_BD_WATER,                        -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
846     &li.bd_water_cannot_flow_down,      FALSE
847   },
848
849   {
850     EL_BD_NUT,                          -1,
851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
852     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
853   },
854
855   {
856     EL_BD_PNEUMATIC_HAMMER,             -1,
857     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
858     &li.bd_hammer_walls_break_delay,    5
859   },
860   {
861     EL_BD_PNEUMATIC_HAMMER,             -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
863     &li.bd_hammer_walls_reappear,       FALSE
864   },
865   {
866     EL_BD_PNEUMATIC_HAMMER,             -1,
867     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
868     &li.bd_hammer_walls_reappear_delay, 100
869   },
870
871   {
872     EL_BD_SKELETON,                     -1,
873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
874     &li.bd_num_skeletons_needed_for_pot, 5
875   },
876   {
877     EL_BD_SKELETON,                     -1,
878     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
879     &li.bd_skeleton_worth_num_diamonds, 0
880   },
881
882   // (the following values are related to various game elements)
883
884   {
885     EL_EMERALD,                         -1,
886     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
887     &li.score[SC_EMERALD],              10
888   },
889
890   {
891     EL_DIAMOND,                         -1,
892     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
893     &li.score[SC_DIAMOND],              10
894   },
895
896   {
897     EL_BUG,                             -1,
898     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
899     &li.score[SC_BUG],                  10
900   },
901
902   {
903     EL_SPACESHIP,                       -1,
904     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
905     &li.score[SC_SPACESHIP],            10
906   },
907
908   {
909     EL_PACMAN,                          -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
911     &li.score[SC_PACMAN],               10
912   },
913
914   {
915     EL_NUT,                             -1,
916     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
917     &li.score[SC_NUT],                  10
918   },
919
920   {
921     EL_DYNAMITE,                        -1,
922     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
923     &li.score[SC_DYNAMITE],             10
924   },
925
926   {
927     EL_KEY_1,                           -1,
928     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
929     &li.score[SC_KEY],                  10
930   },
931
932   {
933     EL_PEARL,                           -1,
934     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
935     &li.score[SC_PEARL],                10
936   },
937
938   {
939     EL_CRYSTAL,                         -1,
940     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
941     &li.score[SC_CRYSTAL],              10
942   },
943
944   // (amoeba values used by R'n'D game engine only)
945   {
946     EL_BD_AMOEBA,                       -1,
947     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
948     &li.amoeba_content,                 EL_DIAMOND
949   },
950   {
951     EL_BD_AMOEBA,                       -1,
952     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
953     &li.amoeba_speed,                   10
954   },
955   {
956     EL_BD_AMOEBA,                       -1,
957     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
958     &li.grow_into_diggable,             TRUE
959   },
960   // (amoeba values used by BD game engine only)
961   {
962     EL_BD_AMOEBA,                       -1,
963     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
964     &li.bd_amoeba_wait_for_hatching,    FALSE
965   },
966   {
967     EL_BD_AMOEBA,                       -1,
968     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
969     &li.bd_amoeba_start_immediately,    TRUE
970   },
971   {
972     EL_BD_AMOEBA,                       -1,
973     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
974     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
975   },
976   {
977     EL_BD_AMOEBA,                       -1,
978     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
979     &li.bd_amoeba_threshold_too_big,    200
980   },
981   {
982     EL_BD_AMOEBA,                       -1,
983     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
984     &li.bd_amoeba_slow_growth_time,     200
985   },
986   {
987     EL_BD_AMOEBA,                       -1,
988     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
989     &li.bd_amoeba_slow_growth_rate,     3
990   },
991   {
992     EL_BD_AMOEBA,                       -1,
993     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
994     &li.bd_amoeba_fast_growth_rate,     25
995   },
996   {
997     EL_BD_AMOEBA,                       -1,
998     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
999     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1000   },
1001   {
1002     EL_BD_AMOEBA,                       -1,
1003     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1004     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1005   },
1006
1007   {
1008     EL_BD_AMOEBA_2,                     -1,
1009     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1010     &li.bd_amoeba_2_threshold_too_big,  200
1011   },
1012   {
1013     EL_BD_AMOEBA_2,                     -1,
1014     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1015     &li.bd_amoeba_2_slow_growth_time,   200
1016   },
1017   {
1018     EL_BD_AMOEBA_2,                     -1,
1019     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1020     &li.bd_amoeba_2_slow_growth_rate,   3
1021   },
1022   {
1023     EL_BD_AMOEBA_2,                     -1,
1024     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1025     &li.bd_amoeba_2_fast_growth_rate,   25
1026   },
1027   {
1028     EL_BD_AMOEBA_2,                     -1,
1029     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1030     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1031   },
1032   {
1033     EL_BD_AMOEBA_2,                     -1,
1034     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1035     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1036   },
1037   {
1038     EL_BD_AMOEBA_2,                     -1,
1039     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1040     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1041   },
1042   {
1043     EL_BD_AMOEBA_2,                     -1,
1044     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1045     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1046   },
1047
1048   {
1049     EL_YAMYAM,                          -1,
1050     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1051     &li.yamyam_content,                 EL_ROCK, NULL,
1052     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1053   },
1054   {
1055     EL_YAMYAM,                          -1,
1056     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1057     &li.score[SC_YAMYAM],               10
1058   },
1059
1060   {
1061     EL_ROBOT,                           -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_ROBOT],                10
1064   },
1065   {
1066     EL_ROBOT,                           -1,
1067     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1068     &li.slurp_score,                    10
1069   },
1070
1071   {
1072     EL_ROBOT_WHEEL,                     -1,
1073     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1074     &li.time_wheel,                     10
1075   },
1076
1077   {
1078     EL_MAGIC_WALL,                      -1,
1079     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1080     &li.time_magic_wall,                10
1081   },
1082
1083   {
1084     EL_GAME_OF_LIFE,                    -1,
1085     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1086     &li.game_of_life[0],                2
1087   },
1088   {
1089     EL_GAME_OF_LIFE,                    -1,
1090     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1091     &li.game_of_life[1],                3
1092   },
1093   {
1094     EL_GAME_OF_LIFE,                    -1,
1095     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1096     &li.game_of_life[2],                3
1097   },
1098   {
1099     EL_GAME_OF_LIFE,                    -1,
1100     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1101     &li.game_of_life[3],                3
1102   },
1103   {
1104     EL_GAME_OF_LIFE,                    -1,
1105     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1106     &li.use_life_bugs,                  FALSE
1107   },
1108
1109   {
1110     EL_BIOMAZE,                         -1,
1111     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1112     &li.biomaze[0],                     2
1113   },
1114   {
1115     EL_BIOMAZE,                         -1,
1116     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1117     &li.biomaze[1],                     3
1118   },
1119   {
1120     EL_BIOMAZE,                         -1,
1121     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1122     &li.biomaze[2],                     3
1123   },
1124   {
1125     EL_BIOMAZE,                         -1,
1126     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1127     &li.biomaze[3],                     3
1128   },
1129
1130   {
1131     EL_TIMEGATE_SWITCH,                 -1,
1132     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1133     &li.time_timegate,                  10
1134   },
1135
1136   {
1137     EL_LIGHT_SWITCH_ACTIVE,             -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.time_light,                     10
1140   },
1141
1142   {
1143     EL_SHIELD_NORMAL,                   -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1145     &li.shield_normal_time,             10
1146   },
1147   {
1148     EL_SHIELD_NORMAL,                   -1,
1149     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1150     &li.score[SC_SHIELD],               10
1151   },
1152
1153   {
1154     EL_SHIELD_DEADLY,                   -1,
1155     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1156     &li.shield_deadly_time,             10
1157   },
1158   {
1159     EL_SHIELD_DEADLY,                   -1,
1160     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1161     &li.score[SC_SHIELD],               10
1162   },
1163
1164   {
1165     EL_EXTRA_TIME,                      -1,
1166     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1167     &li.extra_time,                     10
1168   },
1169   {
1170     EL_EXTRA_TIME,                      -1,
1171     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1172     &li.extra_time_score,               10
1173   },
1174
1175   {
1176     EL_TIME_ORB_FULL,                   -1,
1177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1178     &li.time_orb_time,                  10
1179   },
1180   {
1181     EL_TIME_ORB_FULL,                   -1,
1182     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1183     &li.use_time_orb_bug,               FALSE
1184   },
1185
1186   {
1187     EL_SPRING,                          -1,
1188     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1189     &li.use_spring_bug,                 FALSE
1190   },
1191
1192   {
1193     EL_EMC_ANDROID,                     -1,
1194     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1195     &li.android_move_time,              10
1196   },
1197   {
1198     EL_EMC_ANDROID,                     -1,
1199     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1200     &li.android_clone_time,             10
1201   },
1202   {
1203     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1204     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1205     &li.android_clone_element[0],       EL_EMPTY, NULL,
1206     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1207   },
1208   {
1209     EL_EMC_ANDROID,                     -1,
1210     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1211     &li.android_clone_element[0],       EL_EMPTY, NULL,
1212     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1213   },
1214
1215   {
1216     EL_EMC_LENSES,                      -1,
1217     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1218     &li.lenses_score,                   10
1219   },
1220   {
1221     EL_EMC_LENSES,                      -1,
1222     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1223     &li.lenses_time,                    10
1224   },
1225
1226   {
1227     EL_EMC_MAGNIFIER,                   -1,
1228     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1229     &li.magnify_score,                  10
1230   },
1231   {
1232     EL_EMC_MAGNIFIER,                   -1,
1233     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1234     &li.magnify_time,                   10
1235   },
1236
1237   {
1238     EL_EMC_MAGIC_BALL,                  -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1240     &li.ball_time,                      10
1241   },
1242   {
1243     EL_EMC_MAGIC_BALL,                  -1,
1244     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1245     &li.ball_random,                    FALSE
1246   },
1247   {
1248     EL_EMC_MAGIC_BALL,                  -1,
1249     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1250     &li.ball_active_initial,            FALSE
1251   },
1252   {
1253     EL_EMC_MAGIC_BALL,                  -1,
1254     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1255     &li.ball_content,                   EL_EMPTY, NULL,
1256     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1257   },
1258
1259   {
1260     EL_SOKOBAN_FIELD_EMPTY,             -1,
1261     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1262     &li.sb_fields_needed,               TRUE
1263   },
1264
1265   {
1266     EL_SOKOBAN_OBJECT,                  -1,
1267     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1268     &li.sb_objects_needed,              TRUE
1269   },
1270
1271   {
1272     EL_MM_MCDUFFIN,                     -1,
1273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1274     &li.mm_laser_red,                   FALSE
1275   },
1276   {
1277     EL_MM_MCDUFFIN,                     -1,
1278     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1279     &li.mm_laser_green,                 FALSE
1280   },
1281   {
1282     EL_MM_MCDUFFIN,                     -1,
1283     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1284     &li.mm_laser_blue,                  TRUE
1285   },
1286
1287   {
1288     EL_DF_LASER,                        -1,
1289     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1290     &li.df_laser_red,                   TRUE
1291   },
1292   {
1293     EL_DF_LASER,                        -1,
1294     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1295     &li.df_laser_green,                 TRUE
1296   },
1297   {
1298     EL_DF_LASER,                        -1,
1299     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1300     &li.df_laser_blue,                  FALSE
1301   },
1302
1303   {
1304     EL_MM_FUSE_ACTIVE,                  -1,
1305     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1306     &li.mm_time_fuse,                   25
1307   },
1308   {
1309     EL_MM_BOMB,                         -1,
1310     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1311     &li.mm_time_bomb,                   75
1312   },
1313
1314   {
1315     EL_MM_GRAY_BALL,                    -1,
1316     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1317     &li.mm_time_ball,                   75
1318   },
1319   {
1320     EL_MM_GRAY_BALL,                    -1,
1321     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1322     &li.mm_ball_choice_mode,            ANIM_RANDOM
1323   },
1324   {
1325     EL_MM_GRAY_BALL,                    -1,
1326     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1327     &li.mm_ball_content,                EL_EMPTY, NULL,
1328     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1329   },
1330   {
1331     EL_MM_GRAY_BALL,                    -1,
1332     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1333     &li.rotate_mm_ball_content,         TRUE
1334   },
1335   {
1336     EL_MM_GRAY_BALL,                    -1,
1337     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1338     &li.explode_mm_ball,                FALSE
1339   },
1340
1341   {
1342     EL_MM_STEEL_BLOCK,                  -1,
1343     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1344     &li.mm_time_block,                  75
1345   },
1346   {
1347     EL_MM_LIGHTBALL,                    -1,
1348     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1349     &li.score[SC_ELEM_BONUS],           10
1350   },
1351
1352   {
1353     -1,                                 -1,
1354     -1,                                 -1,
1355     NULL,                               -1
1356   }
1357 };
1358
1359 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1360 {
1361   {
1362     -1,                                 -1,
1363     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1364     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1365   },
1366   {
1367     -1,                                 -1,
1368     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1369     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1370   },
1371
1372   {
1373     -1,                                 -1,
1374     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1375     &xx_envelope.autowrap,              FALSE
1376   },
1377   {
1378     -1,                                 -1,
1379     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1380     &xx_envelope.centered,              FALSE
1381   },
1382
1383   {
1384     -1,                                 -1,
1385     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1386     &xx_envelope.text,                  -1, NULL,
1387     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1388     &xx_default_string_empty[0]
1389   },
1390
1391   {
1392     -1,                                 -1,
1393     -1,                                 -1,
1394     NULL,                               -1
1395   }
1396 };
1397
1398 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1399 {
1400   {
1401     -1,                                 -1,
1402     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1403     &xx_ei.description[0],              -1,
1404     &yy_ei.description[0],
1405     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1406     &xx_default_description[0]
1407   },
1408
1409   {
1410     -1,                                 -1,
1411     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1412     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1413     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1414   },
1415 #if ENABLE_RESERVED_CODE
1416   // (reserved for later use)
1417   {
1418     -1,                                 -1,
1419     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1420     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1421     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1422   },
1423 #endif
1424
1425   {
1426     -1,                                 -1,
1427     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1428     &xx_ei.use_gfx_element,             FALSE,
1429     &yy_ei.use_gfx_element
1430   },
1431   {
1432     -1,                                 -1,
1433     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1434     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1435     &yy_ei.gfx_element_initial
1436   },
1437
1438   {
1439     -1,                                 -1,
1440     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1441     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1442     &yy_ei.access_direction
1443   },
1444
1445   {
1446     -1,                                 -1,
1447     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1448     &xx_ei.collect_score_initial,       10,
1449     &yy_ei.collect_score_initial
1450   },
1451   {
1452     -1,                                 -1,
1453     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1454     &xx_ei.collect_count_initial,       1,
1455     &yy_ei.collect_count_initial
1456   },
1457
1458   {
1459     -1,                                 -1,
1460     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1461     &xx_ei.ce_value_fixed_initial,      0,
1462     &yy_ei.ce_value_fixed_initial
1463   },
1464   {
1465     -1,                                 -1,
1466     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1467     &xx_ei.ce_value_random_initial,     0,
1468     &yy_ei.ce_value_random_initial
1469   },
1470   {
1471     -1,                                 -1,
1472     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1473     &xx_ei.use_last_ce_value,           FALSE,
1474     &yy_ei.use_last_ce_value
1475   },
1476
1477   {
1478     -1,                                 -1,
1479     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1480     &xx_ei.push_delay_fixed,            8,
1481     &yy_ei.push_delay_fixed
1482   },
1483   {
1484     -1,                                 -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1486     &xx_ei.push_delay_random,           8,
1487     &yy_ei.push_delay_random
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1492     &xx_ei.drop_delay_fixed,            0,
1493     &yy_ei.drop_delay_fixed
1494   },
1495   {
1496     -1,                                 -1,
1497     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1498     &xx_ei.drop_delay_random,           0,
1499     &yy_ei.drop_delay_random
1500   },
1501   {
1502     -1,                                 -1,
1503     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1504     &xx_ei.move_delay_fixed,            0,
1505     &yy_ei.move_delay_fixed
1506   },
1507   {
1508     -1,                                 -1,
1509     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1510     &xx_ei.move_delay_random,           0,
1511     &yy_ei.move_delay_random
1512   },
1513   {
1514     -1,                                 -1,
1515     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1516     &xx_ei.step_delay_fixed,            0,
1517     &yy_ei.step_delay_fixed
1518   },
1519   {
1520     -1,                                 -1,
1521     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1522     &xx_ei.step_delay_random,           0,
1523     &yy_ei.step_delay_random
1524   },
1525
1526   {
1527     -1,                                 -1,
1528     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1529     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1530     &yy_ei.move_pattern
1531   },
1532   {
1533     -1,                                 -1,
1534     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1535     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1536     &yy_ei.move_direction_initial
1537   },
1538   {
1539     -1,                                 -1,
1540     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1541     &xx_ei.move_stepsize,               TILEX / 8,
1542     &yy_ei.move_stepsize
1543   },
1544
1545   {
1546     -1,                                 -1,
1547     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1548     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1549     &yy_ei.move_enter_element
1550   },
1551   {
1552     -1,                                 -1,
1553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1554     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1555     &yy_ei.move_leave_element
1556   },
1557   {
1558     -1,                                 -1,
1559     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1560     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1561     &yy_ei.move_leave_type
1562   },
1563
1564   {
1565     -1,                                 -1,
1566     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1567     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1568     &yy_ei.slippery_type
1569   },
1570
1571   {
1572     -1,                                 -1,
1573     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1574     &xx_ei.explosion_type,              EXPLODES_3X3,
1575     &yy_ei.explosion_type
1576   },
1577   {
1578     -1,                                 -1,
1579     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1580     &xx_ei.explosion_delay,             16,
1581     &yy_ei.explosion_delay
1582   },
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1586     &xx_ei.ignition_delay,              8,
1587     &yy_ei.ignition_delay
1588   },
1589
1590   {
1591     -1,                                 -1,
1592     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1593     &xx_ei.content,                     EL_EMPTY_SPACE,
1594     &yy_ei.content,
1595     &xx_num_contents,                   1, 1
1596   },
1597
1598   // ---------- "num_change_pages" must be the last entry ---------------------
1599
1600   {
1601     -1,                                 SAVE_CONF_ALWAYS,
1602     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1603     &xx_ei.num_change_pages,            1,
1604     &yy_ei.num_change_pages
1605   },
1606
1607   {
1608     -1,                                 -1,
1609     -1,                                 -1,
1610     NULL,                               -1,
1611     NULL
1612   }
1613 };
1614
1615 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1616 {
1617   // ---------- "current_change_page" must be the first entry -----------------
1618
1619   {
1620     -1,                                 SAVE_CONF_ALWAYS,
1621     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1622     &xx_current_change_page,            -1
1623   },
1624
1625   // ---------- (the remaining entries can be in any order) -------------------
1626
1627   {
1628     -1,                                 -1,
1629     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1630     &xx_change.can_change,              FALSE
1631   },
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1636     &xx_event_bits[0],                  0
1637   },
1638   {
1639     -1,                                 -1,
1640     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1641     &xx_event_bits[1],                  0
1642   },
1643
1644   {
1645     -1,                                 -1,
1646     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1647     &xx_change.trigger_player,          CH_PLAYER_ANY
1648   },
1649   {
1650     -1,                                 -1,
1651     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1652     &xx_change.trigger_side,            CH_SIDE_ANY
1653   },
1654   {
1655     -1,                                 -1,
1656     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1657     &xx_change.trigger_page,            CH_PAGE_ANY
1658   },
1659
1660   {
1661     -1,                                 -1,
1662     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1663     &xx_change.target_element,          EL_EMPTY_SPACE
1664   },
1665
1666   {
1667     -1,                                 -1,
1668     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1669     &xx_change.delay_fixed,             0
1670   },
1671   {
1672     -1,                                 -1,
1673     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1674     &xx_change.delay_random,            0
1675   },
1676   {
1677     -1,                                 -1,
1678     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1679     &xx_change.delay_frames,            FRAMES_PER_SECOND
1680   },
1681
1682   {
1683     -1,                                 -1,
1684     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1685     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1686   },
1687
1688   {
1689     -1,                                 -1,
1690     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1691     &xx_change.explode,                 FALSE
1692   },
1693   {
1694     -1,                                 -1,
1695     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1696     &xx_change.use_target_content,      FALSE
1697   },
1698   {
1699     -1,                                 -1,
1700     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1701     &xx_change.only_if_complete,        FALSE
1702   },
1703   {
1704     -1,                                 -1,
1705     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1706     &xx_change.use_random_replace,      FALSE
1707   },
1708   {
1709     -1,                                 -1,
1710     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1711     &xx_change.random_percentage,       100
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1716     &xx_change.replace_when,            CP_WHEN_EMPTY
1717   },
1718
1719   {
1720     -1,                                 -1,
1721     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1722     &xx_change.has_action,              FALSE
1723   },
1724   {
1725     -1,                                 -1,
1726     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1727     &xx_change.action_type,             CA_NO_ACTION
1728   },
1729   {
1730     -1,                                 -1,
1731     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1732     &xx_change.action_mode,             CA_MODE_UNDEFINED
1733   },
1734   {
1735     -1,                                 -1,
1736     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1737     &xx_change.action_arg,              CA_ARG_UNDEFINED
1738   },
1739
1740   {
1741     -1,                                 -1,
1742     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1743     &xx_change.action_element,          EL_EMPTY_SPACE
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1749     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1750     &xx_num_contents,                   1, 1
1751   },
1752
1753   {
1754     -1,                                 -1,
1755     -1,                                 -1,
1756     NULL,                               -1
1757   }
1758 };
1759
1760 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1761 {
1762   {
1763     -1,                                 -1,
1764     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1765     &xx_ei.description[0],              -1, NULL,
1766     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1767     &xx_default_description[0]
1768   },
1769
1770   {
1771     -1,                                 -1,
1772     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1773     &xx_ei.use_gfx_element,             FALSE
1774   },
1775   {
1776     -1,                                 -1,
1777     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1778     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1779   },
1780
1781   {
1782     -1,                                 -1,
1783     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1784     &xx_group.choice_mode,              ANIM_RANDOM
1785   },
1786
1787   {
1788     -1,                                 -1,
1789     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1790     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1791     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1792   },
1793
1794   {
1795     -1,                                 -1,
1796     -1,                                 -1,
1797     NULL,                               -1
1798   }
1799 };
1800
1801 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1802 {
1803   {
1804     -1,                                 -1,
1805     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1806     &xx_ei.use_gfx_element,             FALSE
1807   },
1808   {
1809     -1,                                 -1,
1810     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1811     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1812   },
1813
1814   {
1815     -1,                                 -1,
1816     -1,                                 -1,
1817     NULL,                               -1
1818   }
1819 };
1820
1821 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1822 {
1823   {
1824     EL_PLAYER_1,                        -1,
1825     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1826     &li.block_snap_field,               TRUE
1827   },
1828   {
1829     EL_PLAYER_1,                        -1,
1830     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1831     &li.continuous_snapping,            TRUE
1832   },
1833   {
1834     EL_PLAYER_1,                        -1,
1835     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1836     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1837   },
1838   {
1839     EL_PLAYER_1,                        -1,
1840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1841     &li.use_start_element[0],           FALSE
1842   },
1843   {
1844     EL_PLAYER_1,                        -1,
1845     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1846     &li.start_element[0],               EL_PLAYER_1
1847   },
1848   {
1849     EL_PLAYER_1,                        -1,
1850     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1851     &li.use_artwork_element[0],         FALSE
1852   },
1853   {
1854     EL_PLAYER_1,                        -1,
1855     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1856     &li.artwork_element[0],             EL_PLAYER_1
1857   },
1858   {
1859     EL_PLAYER_1,                        -1,
1860     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1861     &li.use_explosion_element[0],       FALSE
1862   },
1863   {
1864     EL_PLAYER_1,                        -1,
1865     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1866     &li.explosion_element[0],           EL_PLAYER_1
1867   },
1868
1869   {
1870     -1,                                 -1,
1871     -1,                                 -1,
1872     NULL,                               -1
1873   }
1874 };
1875
1876 static struct
1877 {
1878   int filetype;
1879   char *id;
1880 }
1881 filetype_id_list[] =
1882 {
1883   { LEVEL_FILE_TYPE_RND,        "RND"   },
1884   { LEVEL_FILE_TYPE_BD,         "BD"    },
1885   { LEVEL_FILE_TYPE_EM,         "EM"    },
1886   { LEVEL_FILE_TYPE_SP,         "SP"    },
1887   { LEVEL_FILE_TYPE_DX,         "DX"    },
1888   { LEVEL_FILE_TYPE_SB,         "SB"    },
1889   { LEVEL_FILE_TYPE_DC,         "DC"    },
1890   { LEVEL_FILE_TYPE_MM,         "MM"    },
1891   { LEVEL_FILE_TYPE_MM,         "DF"    },
1892   { -1,                         NULL    },
1893 };
1894
1895
1896 // ============================================================================
1897 // level file functions
1898 // ============================================================================
1899
1900 static boolean check_special_flags(char *flag)
1901 {
1902   if (strEqual(options.special_flags, flag) ||
1903       strEqual(leveldir_current->special_flags, flag))
1904     return TRUE;
1905
1906   return FALSE;
1907 }
1908
1909 static struct DateInfo getCurrentDate(void)
1910 {
1911   time_t epoch_seconds = time(NULL);
1912   struct tm *now = localtime(&epoch_seconds);
1913   struct DateInfo date;
1914
1915   date.year  = now->tm_year + 1900;
1916   date.month = now->tm_mon  + 1;
1917   date.day   = now->tm_mday;
1918
1919   date.src   = DATE_SRC_CLOCK;
1920
1921   return date;
1922 }
1923
1924 static void resetEventFlags(struct ElementChangeInfo *change)
1925 {
1926   int i;
1927
1928   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1929     change->has_event[i] = FALSE;
1930 }
1931
1932 static void resetEventBits(void)
1933 {
1934   int i;
1935
1936   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1937     xx_event_bits[i] = 0;
1938 }
1939
1940 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1941 {
1942   int i;
1943
1944   /* important: only change event flag if corresponding event bit is set
1945      (this is because all xx_event_bits[] values are loaded separately,
1946      and all xx_event_bits[] values are set back to zero before loading
1947      another value xx_event_bits[x] (each value representing 32 flags)) */
1948
1949   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1950     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1951       change->has_event[i] = TRUE;
1952 }
1953
1954 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1955 {
1956   int i;
1957
1958   /* in contrast to the above function setEventFlagsFromEventBits(), it
1959      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1960      depending on the corresponding change->has_event[i] values here, as
1961      all xx_event_bits[] values are reset in resetEventBits() before */
1962
1963   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1964     if (change->has_event[i])
1965       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1966 }
1967
1968 static char *getDefaultElementDescription(struct ElementInfo *ei)
1969 {
1970   static char description[MAX_ELEMENT_NAME_LEN + 1];
1971   char *default_description = (ei->custom_description != NULL ?
1972                                ei->custom_description :
1973                                ei->editor_description);
1974   int i;
1975
1976   // always start with reliable default values
1977   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1978     description[i] = '\0';
1979
1980   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1981   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1982
1983   return &description[0];
1984 }
1985
1986 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1987 {
1988   char *default_description = getDefaultElementDescription(ei);
1989   int i;
1990
1991   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1992     ei->description[i] = default_description[i];
1993 }
1994
1995 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1996 {
1997   int i;
1998
1999   for (i = 0; conf[i].data_type != -1; i++)
2000   {
2001     int default_value = conf[i].default_value;
2002     int data_type = conf[i].data_type;
2003     int conf_type = conf[i].conf_type;
2004     int byte_mask = conf_type & CONF_MASK_BYTES;
2005
2006     if (byte_mask == CONF_MASK_MULTI_BYTES)
2007     {
2008       int default_num_entities = conf[i].default_num_entities;
2009       int max_num_entities = conf[i].max_num_entities;
2010
2011       *(int *)(conf[i].num_entities) = default_num_entities;
2012
2013       if (data_type == TYPE_STRING)
2014       {
2015         char *default_string = conf[i].default_string;
2016         char *string = (char *)(conf[i].value);
2017
2018         strncpy(string, default_string, max_num_entities);
2019       }
2020       else if (data_type == TYPE_ELEMENT_LIST)
2021       {
2022         int *element_array = (int *)(conf[i].value);
2023         int j;
2024
2025         for (j = 0; j < max_num_entities; j++)
2026           element_array[j] = default_value;
2027       }
2028       else if (data_type == TYPE_CONTENT_LIST)
2029       {
2030         struct Content *content = (struct Content *)(conf[i].value);
2031         int c, x, y;
2032
2033         for (c = 0; c < max_num_entities; c++)
2034           for (y = 0; y < 3; y++)
2035             for (x = 0; x < 3; x++)
2036               content[c].e[x][y] = default_value;
2037       }
2038     }
2039     else        // constant size configuration data (1, 2 or 4 bytes)
2040     {
2041       if (data_type == TYPE_BOOLEAN)
2042         *(boolean *)(conf[i].value) = default_value;
2043       else
2044         *(int *)    (conf[i].value) = default_value;
2045     }
2046   }
2047 }
2048
2049 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2050 {
2051   int i;
2052
2053   for (i = 0; conf[i].data_type != -1; i++)
2054   {
2055     int data_type = conf[i].data_type;
2056     int conf_type = conf[i].conf_type;
2057     int byte_mask = conf_type & CONF_MASK_BYTES;
2058
2059     if (byte_mask == CONF_MASK_MULTI_BYTES)
2060     {
2061       int max_num_entities = conf[i].max_num_entities;
2062
2063       if (data_type == TYPE_STRING)
2064       {
2065         char *string      = (char *)(conf[i].value);
2066         char *string_copy = (char *)(conf[i].value_copy);
2067
2068         strncpy(string_copy, string, max_num_entities);
2069       }
2070       else if (data_type == TYPE_ELEMENT_LIST)
2071       {
2072         int *element_array      = (int *)(conf[i].value);
2073         int *element_array_copy = (int *)(conf[i].value_copy);
2074         int j;
2075
2076         for (j = 0; j < max_num_entities; j++)
2077           element_array_copy[j] = element_array[j];
2078       }
2079       else if (data_type == TYPE_CONTENT_LIST)
2080       {
2081         struct Content *content      = (struct Content *)(conf[i].value);
2082         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2083         int c, x, y;
2084
2085         for (c = 0; c < max_num_entities; c++)
2086           for (y = 0; y < 3; y++)
2087             for (x = 0; x < 3; x++)
2088               content_copy[c].e[x][y] = content[c].e[x][y];
2089       }
2090     }
2091     else        // constant size configuration data (1, 2 or 4 bytes)
2092     {
2093       if (data_type == TYPE_BOOLEAN)
2094         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2095       else
2096         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2097     }
2098   }
2099 }
2100
2101 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2102 {
2103   int i;
2104
2105   xx_ei = *ei_from;     // copy element data into temporary buffer
2106   yy_ei = *ei_to;       // copy element data into temporary buffer
2107
2108   copyConfigFromConfigList(chunk_config_CUSX_base);
2109
2110   *ei_from = xx_ei;
2111   *ei_to   = yy_ei;
2112
2113   // ---------- reinitialize and copy change pages ----------
2114
2115   ei_to->num_change_pages = ei_from->num_change_pages;
2116   ei_to->current_change_page = ei_from->current_change_page;
2117
2118   setElementChangePages(ei_to, ei_to->num_change_pages);
2119
2120   for (i = 0; i < ei_to->num_change_pages; i++)
2121     ei_to->change_page[i] = ei_from->change_page[i];
2122
2123   // ---------- copy group element info ----------
2124   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2125     *ei_to->group = *ei_from->group;
2126
2127   // mark this custom element as modified
2128   ei_to->modified_settings = TRUE;
2129 }
2130
2131 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2132 {
2133   int change_page_size = sizeof(struct ElementChangeInfo);
2134
2135   ei->num_change_pages = MAX(1, change_pages);
2136
2137   ei->change_page =
2138     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2139
2140   if (ei->current_change_page >= ei->num_change_pages)
2141     ei->current_change_page = ei->num_change_pages - 1;
2142
2143   ei->change = &ei->change_page[ei->current_change_page];
2144 }
2145
2146 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2147 {
2148   xx_change = *change;          // copy change data into temporary buffer
2149
2150   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2151
2152   *change = xx_change;
2153
2154   resetEventFlags(change);
2155
2156   change->direct_action = 0;
2157   change->other_action = 0;
2158
2159   change->pre_change_function = NULL;
2160   change->change_function = NULL;
2161   change->post_change_function = NULL;
2162 }
2163
2164 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2165 {
2166   int i, x, y;
2167
2168   li = *level;          // copy level data into temporary buffer
2169   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2170   *level = li;          // copy temporary buffer back to level data
2171
2172   setLevelInfoToDefaults_BD();
2173   setLevelInfoToDefaults_EM();
2174   setLevelInfoToDefaults_SP();
2175   setLevelInfoToDefaults_MM();
2176
2177   level->native_bd_level = &native_bd_level;
2178   level->native_em_level = &native_em_level;
2179   level->native_sp_level = &native_sp_level;
2180   level->native_mm_level = &native_mm_level;
2181
2182   level->file_version = FILE_VERSION_ACTUAL;
2183   level->game_version = GAME_VERSION_ACTUAL;
2184
2185   level->creation_date = getCurrentDate();
2186
2187   level->encoding_16bit_field  = TRUE;
2188   level->encoding_16bit_yamyam = TRUE;
2189   level->encoding_16bit_amoeba = TRUE;
2190
2191   // clear level name and level author string buffers
2192   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2193     level->name[i] = '\0';
2194   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2195     level->author[i] = '\0';
2196
2197   // set level name and level author to default values
2198   strcpy(level->name, NAMELESS_LEVEL_NAME);
2199   strcpy(level->author, ANONYMOUS_NAME);
2200
2201   // set level playfield to playable default level with player and exit
2202   for (x = 0; x < MAX_LEV_FIELDX; x++)
2203     for (y = 0; y < MAX_LEV_FIELDY; y++)
2204       level->field[x][y] = EL_SAND;
2205
2206   level->field[0][0] = EL_PLAYER_1;
2207   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2208
2209   BorderElement = EL_STEELWALL;
2210
2211   // detect custom elements when loading them
2212   level->file_has_custom_elements = FALSE;
2213
2214   // set all bug compatibility flags to "false" => do not emulate this bug
2215   level->use_action_after_change_bug = FALSE;
2216
2217   if (leveldir_current)
2218   {
2219     // try to determine better author name than 'anonymous'
2220     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2221     {
2222       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2223       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2224     }
2225     else
2226     {
2227       switch (LEVELCLASS(leveldir_current))
2228       {
2229         case LEVELCLASS_TUTORIAL:
2230           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2231           break;
2232
2233         case LEVELCLASS_CONTRIB:
2234           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2235           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2236           break;
2237
2238         case LEVELCLASS_PRIVATE:
2239           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2240           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2241           break;
2242
2243         default:
2244           // keep default value
2245           break;
2246       }
2247     }
2248   }
2249 }
2250
2251 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2252 {
2253   static boolean clipboard_elements_initialized = FALSE;
2254   int i;
2255
2256   InitElementPropertiesStatic();
2257
2258   li = *level;          // copy level data into temporary buffer
2259   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2260   *level = li;          // copy temporary buffer back to level data
2261
2262   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2263   {
2264     int element = i;
2265     struct ElementInfo *ei = &element_info[element];
2266
2267     if (element == EL_MM_GRAY_BALL)
2268     {
2269       struct LevelInfo_MM *level_mm = level->native_mm_level;
2270       int j;
2271
2272       for (j = 0; j < level->num_mm_ball_contents; j++)
2273         level->mm_ball_content[j] =
2274           map_element_MM_to_RND(level_mm->ball_content[j]);
2275     }
2276
2277     // never initialize clipboard elements after the very first time
2278     // (to be able to use clipboard elements between several levels)
2279     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2280       continue;
2281
2282     if (IS_ENVELOPE(element))
2283     {
2284       int envelope_nr = element - EL_ENVELOPE_1;
2285
2286       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2287
2288       level->envelope[envelope_nr] = xx_envelope;
2289     }
2290
2291     if (IS_CUSTOM_ELEMENT(element) ||
2292         IS_GROUP_ELEMENT(element) ||
2293         IS_INTERNAL_ELEMENT(element))
2294     {
2295       xx_ei = *ei;      // copy element data into temporary buffer
2296
2297       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2298
2299       *ei = xx_ei;
2300     }
2301
2302     setElementChangePages(ei, 1);
2303     setElementChangeInfoToDefaults(ei->change);
2304
2305     if (IS_CUSTOM_ELEMENT(element) ||
2306         IS_GROUP_ELEMENT(element))
2307     {
2308       setElementDescriptionToDefault(ei);
2309
2310       ei->modified_settings = FALSE;
2311     }
2312
2313     if (IS_CUSTOM_ELEMENT(element) ||
2314         IS_INTERNAL_ELEMENT(element))
2315     {
2316       // internal values used in level editor
2317
2318       ei->access_type = 0;
2319       ei->access_layer = 0;
2320       ei->access_protected = 0;
2321       ei->walk_to_action = 0;
2322       ei->smash_targets = 0;
2323       ei->deadliness = 0;
2324
2325       ei->can_explode_by_fire = FALSE;
2326       ei->can_explode_smashed = FALSE;
2327       ei->can_explode_impact = FALSE;
2328
2329       ei->current_change_page = 0;
2330     }
2331
2332     if (IS_GROUP_ELEMENT(element) ||
2333         IS_INTERNAL_ELEMENT(element))
2334     {
2335       struct ElementGroupInfo *group;
2336
2337       // initialize memory for list of elements in group
2338       if (ei->group == NULL)
2339         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2340
2341       group = ei->group;
2342
2343       xx_group = *group;        // copy group data into temporary buffer
2344
2345       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2346
2347       *group = xx_group;
2348     }
2349
2350     if (IS_EMPTY_ELEMENT(element) ||
2351         IS_INTERNAL_ELEMENT(element))
2352     {
2353       xx_ei = *ei;              // copy element data into temporary buffer
2354
2355       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2356
2357       *ei = xx_ei;
2358     }
2359   }
2360
2361   clipboard_elements_initialized = TRUE;
2362 }
2363
2364 static void setLevelInfoToDefaults(struct LevelInfo *level,
2365                                    boolean level_info_only,
2366                                    boolean reset_file_status)
2367 {
2368   setLevelInfoToDefaults_Level(level);
2369
2370   if (!level_info_only)
2371     setLevelInfoToDefaults_Elements(level);
2372
2373   if (reset_file_status)
2374   {
2375     level->no_valid_file = FALSE;
2376     level->no_level_file = FALSE;
2377   }
2378
2379   level->changed = FALSE;
2380 }
2381
2382 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2383 {
2384   level_file_info->nr = 0;
2385   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2386   level_file_info->packed = FALSE;
2387
2388   setString(&level_file_info->basename, NULL);
2389   setString(&level_file_info->filename, NULL);
2390 }
2391
2392 int getMappedElement_SB(int, boolean);
2393
2394 static void ActivateLevelTemplate(void)
2395 {
2396   int x, y;
2397
2398   if (check_special_flags("load_xsb_to_ces"))
2399   {
2400     // fill smaller playfields with padding "beyond border wall" elements
2401     if (level.fieldx < level_template.fieldx ||
2402         level.fieldy < level_template.fieldy)
2403     {
2404       short field[level.fieldx][level.fieldy];
2405       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2406       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2407       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2408       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2409
2410       // copy old playfield (which is smaller than the visible area)
2411       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2412         field[x][y] = level.field[x][y];
2413
2414       // fill new, larger playfield with "beyond border wall" elements
2415       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2416         level.field[x][y] = getMappedElement_SB('_', TRUE);
2417
2418       // copy the old playfield to the middle of the new playfield
2419       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2420         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2421
2422       level.fieldx = new_fieldx;
2423       level.fieldy = new_fieldy;
2424     }
2425   }
2426
2427   // Currently there is no special action needed to activate the template
2428   // data, because 'element_info' property settings overwrite the original
2429   // level data, while all other variables do not change.
2430
2431   // Exception: 'from_level_template' elements in the original level playfield
2432   // are overwritten with the corresponding elements at the same position in
2433   // playfield from the level template.
2434
2435   for (x = 0; x < level.fieldx; x++)
2436     for (y = 0; y < level.fieldy; y++)
2437       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2438         level.field[x][y] = level_template.field[x][y];
2439
2440   if (check_special_flags("load_xsb_to_ces"))
2441   {
2442     struct LevelInfo level_backup = level;
2443
2444     // overwrite all individual level settings from template level settings
2445     level = level_template;
2446
2447     // restore level file info
2448     level.file_info = level_backup.file_info;
2449
2450     // restore playfield size
2451     level.fieldx = level_backup.fieldx;
2452     level.fieldy = level_backup.fieldy;
2453
2454     // restore playfield content
2455     for (x = 0; x < level.fieldx; x++)
2456       for (y = 0; y < level.fieldy; y++)
2457         level.field[x][y] = level_backup.field[x][y];
2458
2459     // restore name and author from individual level
2460     strcpy(level.name,   level_backup.name);
2461     strcpy(level.author, level_backup.author);
2462
2463     // restore flag "use_custom_template"
2464     level.use_custom_template = level_backup.use_custom_template;
2465   }
2466 }
2467
2468 static boolean checkForPackageFromBasename_BD(char *basename)
2469 {
2470   // check for native BD level file extensions
2471   if (!strSuffixLower(basename, ".bd") &&
2472       !strSuffixLower(basename, ".bdr") &&
2473       !strSuffixLower(basename, ".brc") &&
2474       !strSuffixLower(basename, ".gds"))
2475     return FALSE;
2476
2477   // check for standard single-level BD files (like "001.bd")
2478   if (strSuffixLower(basename, ".bd") &&
2479       strlen(basename) == 6 &&
2480       basename[0] >= '0' && basename[0] <= '9' &&
2481       basename[1] >= '0' && basename[1] <= '9' &&
2482       basename[2] >= '0' && basename[2] <= '9')
2483     return FALSE;
2484
2485   // this is a level package in native BD file format
2486   return TRUE;
2487 }
2488
2489 static char *getLevelFilenameFromBasename(char *basename)
2490 {
2491   static char *filename = NULL;
2492
2493   checked_free(filename);
2494
2495   filename = getPath2(getCurrentLevelDir(), basename);
2496
2497   return filename;
2498 }
2499
2500 static int getFileTypeFromBasename(char *basename)
2501 {
2502   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2503
2504   static char *filename = NULL;
2505   struct stat file_status;
2506
2507   // ---------- try to determine file type from filename ----------
2508
2509   // check for typical filename of a Supaplex level package file
2510   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2511     return LEVEL_FILE_TYPE_SP;
2512
2513   // check for typical filename of a Diamond Caves II level package file
2514   if (strSuffixLower(basename, ".dc") ||
2515       strSuffixLower(basename, ".dc2"))
2516     return LEVEL_FILE_TYPE_DC;
2517
2518   // check for typical filename of a Sokoban level package file
2519   if (strSuffixLower(basename, ".xsb") &&
2520       strchr(basename, '%') == NULL)
2521     return LEVEL_FILE_TYPE_SB;
2522
2523   // check for typical filename of a Boulder Dash (GDash) level package file
2524   if (checkForPackageFromBasename_BD(basename))
2525     return LEVEL_FILE_TYPE_BD;
2526
2527   // ---------- try to determine file type from filesize ----------
2528
2529   checked_free(filename);
2530   filename = getPath2(getCurrentLevelDir(), basename);
2531
2532   if (stat(filename, &file_status) == 0)
2533   {
2534     // check for typical filesize of a Supaplex level package file
2535     if (file_status.st_size == 170496)
2536       return LEVEL_FILE_TYPE_SP;
2537   }
2538
2539   return LEVEL_FILE_TYPE_UNKNOWN;
2540 }
2541
2542 static int getFileTypeFromMagicBytes(char *filename, int type)
2543 {
2544   File *file;
2545
2546   if ((file = openFile(filename, MODE_READ)))
2547   {
2548     char chunk_name[CHUNK_ID_LEN + 1];
2549
2550     getFileChunkBE(file, chunk_name, NULL);
2551
2552     if (strEqual(chunk_name, "MMII") ||
2553         strEqual(chunk_name, "MIRR"))
2554       type = LEVEL_FILE_TYPE_MM;
2555
2556     closeFile(file);
2557   }
2558
2559   return type;
2560 }
2561
2562 static boolean checkForPackageFromBasename(char *basename)
2563 {
2564   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2565   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2566
2567   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2568 }
2569
2570 static char *getSingleLevelBasenameExt(int nr, char *extension)
2571 {
2572   static char basename[MAX_FILENAME_LEN];
2573
2574   if (nr < 0)
2575     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2576   else
2577     sprintf(basename, "%03d.%s", nr, extension);
2578
2579   return basename;
2580 }
2581
2582 static char *getSingleLevelBasename(int nr)
2583 {
2584   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2585 }
2586
2587 static char *getPackedLevelBasename(int type)
2588 {
2589   static char basename[MAX_FILENAME_LEN];
2590   char *directory = getCurrentLevelDir();
2591   Directory *dir;
2592   DirectoryEntry *dir_entry;
2593
2594   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2595
2596   if ((dir = openDirectory(directory)) == NULL)
2597   {
2598     Warn("cannot read current level directory '%s'", directory);
2599
2600     return basename;
2601   }
2602
2603   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2604   {
2605     char *entry_basename = dir_entry->basename;
2606     int entry_type = getFileTypeFromBasename(entry_basename);
2607
2608     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2609     {
2610       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2611           type == entry_type)
2612       {
2613         strcpy(basename, entry_basename);
2614
2615         break;
2616       }
2617     }
2618   }
2619
2620   closeDirectory(dir);
2621
2622   return basename;
2623 }
2624
2625 static char *getSingleLevelFilename(int nr)
2626 {
2627   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2628 }
2629
2630 #if ENABLE_UNUSED_CODE
2631 static char *getPackedLevelFilename(int type)
2632 {
2633   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2634 }
2635 #endif
2636
2637 char *getDefaultLevelFilename(int nr)
2638 {
2639   return getSingleLevelFilename(nr);
2640 }
2641
2642 #if ENABLE_UNUSED_CODE
2643 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2644                                                  int type)
2645 {
2646   lfi->type = type;
2647   lfi->packed = FALSE;
2648
2649   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2650   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2651 }
2652 #endif
2653
2654 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2655                                                  int type, char *format, ...)
2656 {
2657   static char basename[MAX_FILENAME_LEN];
2658   va_list ap;
2659
2660   va_start(ap, format);
2661   vsprintf(basename, format, ap);
2662   va_end(ap);
2663
2664   lfi->type = type;
2665   lfi->packed = FALSE;
2666
2667   setString(&lfi->basename, basename);
2668   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2669 }
2670
2671 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2672                                                  int type)
2673 {
2674   lfi->type = type;
2675   lfi->packed = TRUE;
2676
2677   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2678   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2679 }
2680
2681 static int getFiletypeFromID(char *filetype_id)
2682 {
2683   char *filetype_id_lower;
2684   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2685   int i;
2686
2687   if (filetype_id == NULL)
2688     return LEVEL_FILE_TYPE_UNKNOWN;
2689
2690   filetype_id_lower = getStringToLower(filetype_id);
2691
2692   for (i = 0; filetype_id_list[i].id != NULL; i++)
2693   {
2694     char *id_lower = getStringToLower(filetype_id_list[i].id);
2695     
2696     if (strEqual(filetype_id_lower, id_lower))
2697       filetype = filetype_id_list[i].filetype;
2698
2699     free(id_lower);
2700
2701     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2702       break;
2703   }
2704
2705   free(filetype_id_lower);
2706
2707   return filetype;
2708 }
2709
2710 char *getLocalLevelTemplateFilename(void)
2711 {
2712   return getDefaultLevelFilename(-1);
2713 }
2714
2715 char *getGlobalLevelTemplateFilename(void)
2716 {
2717   // global variable "leveldir_current" must be modified in the loop below
2718   LevelDirTree *leveldir_current_last = leveldir_current;
2719   char *filename = NULL;
2720
2721   // check for template level in path from current to topmost tree node
2722
2723   while (leveldir_current != NULL)
2724   {
2725     filename = getDefaultLevelFilename(-1);
2726
2727     if (fileExists(filename))
2728       break;
2729
2730     leveldir_current = leveldir_current->node_parent;
2731   }
2732
2733   // restore global variable "leveldir_current" modified in above loop
2734   leveldir_current = leveldir_current_last;
2735
2736   return filename;
2737 }
2738
2739 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2740 {
2741   int nr = lfi->nr;
2742
2743   // special case: level number is negative => check for level template file
2744   if (nr < 0)
2745   {
2746     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2747                                          getSingleLevelBasename(-1));
2748
2749     // replace local level template filename with global template filename
2750     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2751
2752     // no fallback if template file not existing
2753     return;
2754   }
2755
2756   // special case: check for file name/pattern specified in "levelinfo.conf"
2757   if (leveldir_current->level_filename != NULL)
2758   {
2759     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2760
2761     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2762                                          leveldir_current->level_filename, nr);
2763
2764     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2765
2766     if (fileExists(lfi->filename))
2767       return;
2768   }
2769   else if (leveldir_current->level_filetype != NULL)
2770   {
2771     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2772
2773     // check for specified native level file with standard file name
2774     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2775                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2776     if (fileExists(lfi->filename))
2777       return;
2778   }
2779
2780   // check for native Rocks'n'Diamonds level file
2781   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2782                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2783   if (fileExists(lfi->filename))
2784     return;
2785
2786   // check for native Boulder Dash level file
2787   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2788   if (fileExists(lfi->filename))
2789     return;
2790
2791   // check for Emerald Mine level file (V1)
2792   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2793                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2794   if (fileExists(lfi->filename))
2795     return;
2796   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2797                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2798   if (fileExists(lfi->filename))
2799     return;
2800
2801   // check for Emerald Mine level file (V2 to V5)
2802   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2803   if (fileExists(lfi->filename))
2804     return;
2805
2806   // check for Emerald Mine level file (V6 / single mode)
2807   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2808   if (fileExists(lfi->filename))
2809     return;
2810   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2811   if (fileExists(lfi->filename))
2812     return;
2813
2814   // check for Emerald Mine level file (V6 / teamwork mode)
2815   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2816   if (fileExists(lfi->filename))
2817     return;
2818   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2819   if (fileExists(lfi->filename))
2820     return;
2821
2822   // check for various packed level file formats
2823   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2824   if (fileExists(lfi->filename))
2825     return;
2826
2827   // no known level file found -- use default values (and fail later)
2828   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2829                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2830 }
2831
2832 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2833 {
2834   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2835     lfi->type = getFileTypeFromBasename(lfi->basename);
2836
2837   if (lfi->type == LEVEL_FILE_TYPE_RND)
2838     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2839 }
2840
2841 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2842 {
2843   // always start with reliable default values
2844   setFileInfoToDefaults(level_file_info);
2845
2846   level_file_info->nr = nr;     // set requested level number
2847
2848   determineLevelFileInfo_Filename(level_file_info);
2849   determineLevelFileInfo_Filetype(level_file_info);
2850 }
2851
2852 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2853                               struct LevelFileInfo *lfi_to)
2854 {
2855   lfi_to->nr     = lfi_from->nr;
2856   lfi_to->type   = lfi_from->type;
2857   lfi_to->packed = lfi_from->packed;
2858
2859   setString(&lfi_to->basename, lfi_from->basename);
2860   setString(&lfi_to->filename, lfi_from->filename);
2861 }
2862
2863 // ----------------------------------------------------------------------------
2864 // functions for loading R'n'D level
2865 // ----------------------------------------------------------------------------
2866
2867 int getMappedElement(int element)
2868 {
2869   // remap some (historic, now obsolete) elements
2870
2871   switch (element)
2872   {
2873     case EL_PLAYER_OBSOLETE:
2874       element = EL_PLAYER_1;
2875       break;
2876
2877     case EL_KEY_OBSOLETE:
2878       element = EL_KEY_1;
2879       break;
2880
2881     case EL_EM_KEY_1_FILE_OBSOLETE:
2882       element = EL_EM_KEY_1;
2883       break;
2884
2885     case EL_EM_KEY_2_FILE_OBSOLETE:
2886       element = EL_EM_KEY_2;
2887       break;
2888
2889     case EL_EM_KEY_3_FILE_OBSOLETE:
2890       element = EL_EM_KEY_3;
2891       break;
2892
2893     case EL_EM_KEY_4_FILE_OBSOLETE:
2894       element = EL_EM_KEY_4;
2895       break;
2896
2897     case EL_ENVELOPE_OBSOLETE:
2898       element = EL_ENVELOPE_1;
2899       break;
2900
2901     case EL_SP_EMPTY:
2902       element = EL_EMPTY;
2903       break;
2904
2905     default:
2906       if (element >= NUM_FILE_ELEMENTS)
2907       {
2908         Warn("invalid level element %d", element);
2909
2910         element = EL_UNKNOWN;
2911       }
2912       break;
2913   }
2914
2915   return element;
2916 }
2917
2918 static int getMappedElementByVersion(int element, int game_version)
2919 {
2920   // remap some elements due to certain game version
2921
2922   if (game_version <= VERSION_IDENT(2,2,0,0))
2923   {
2924     // map game font elements
2925     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2926                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2927                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2928                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2929   }
2930
2931   if (game_version < VERSION_IDENT(3,0,0,0))
2932   {
2933     // map Supaplex gravity tube elements
2934     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2935                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2936                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2937                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2938                element);
2939   }
2940
2941   return element;
2942 }
2943
2944 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2945 {
2946   level->file_version = getFileVersion(file);
2947   level->game_version = getFileVersion(file);
2948
2949   return chunk_size;
2950 }
2951
2952 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2953 {
2954   level->creation_date.year  = getFile16BitBE(file);
2955   level->creation_date.month = getFile8Bit(file);
2956   level->creation_date.day   = getFile8Bit(file);
2957
2958   level->creation_date.src   = DATE_SRC_LEVELFILE;
2959
2960   return chunk_size;
2961 }
2962
2963 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2964 {
2965   int initial_player_stepsize;
2966   int initial_player_gravity;
2967   int i, x, y;
2968
2969   level->fieldx = getFile8Bit(file);
2970   level->fieldy = getFile8Bit(file);
2971
2972   level->time           = getFile16BitBE(file);
2973   level->gems_needed    = getFile16BitBE(file);
2974
2975   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2976     level->name[i] = getFile8Bit(file);
2977   level->name[MAX_LEVEL_NAME_LEN] = 0;
2978
2979   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2980     level->score[i] = getFile8Bit(file);
2981
2982   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2983   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2984     for (y = 0; y < 3; y++)
2985       for (x = 0; x < 3; x++)
2986         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2987
2988   level->amoeba_speed           = getFile8Bit(file);
2989   level->time_magic_wall        = getFile8Bit(file);
2990   level->time_wheel             = getFile8Bit(file);
2991   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2992
2993   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2994                                    STEPSIZE_NORMAL);
2995
2996   for (i = 0; i < MAX_PLAYERS; i++)
2997     level->initial_player_stepsize[i] = initial_player_stepsize;
2998
2999   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3000
3001   for (i = 0; i < MAX_PLAYERS; i++)
3002     level->initial_player_gravity[i] = initial_player_gravity;
3003
3004   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3005   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3006
3007   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3008
3009   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3010   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011   level->can_move_into_acid_bits = getFile32BitBE(file);
3012   level->dont_collide_with_bits = getFile8Bit(file);
3013
3014   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3015   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016
3017   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3018   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3019   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3020
3021   level->game_engine_type       = getFile8Bit(file);
3022
3023   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3024
3025   return chunk_size;
3026 }
3027
3028 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3029 {
3030   int i;
3031
3032   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3033     level->name[i] = getFile8Bit(file);
3034   level->name[MAX_LEVEL_NAME_LEN] = 0;
3035
3036   return chunk_size;
3037 }
3038
3039 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3040 {
3041   int i;
3042
3043   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3044     level->author[i] = getFile8Bit(file);
3045   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3046
3047   return chunk_size;
3048 }
3049
3050 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3051 {
3052   int x, y;
3053   int chunk_size_expected = level->fieldx * level->fieldy;
3054
3055   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3056      stored with 16-bit encoding (and should be twice as big then).
3057      Even worse, playfield data was stored 16-bit when only yamyam content
3058      contained 16-bit elements and vice versa. */
3059
3060   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3061     chunk_size_expected *= 2;
3062
3063   if (chunk_size_expected != chunk_size)
3064   {
3065     ReadUnusedBytesFromFile(file, chunk_size);
3066     return chunk_size_expected;
3067   }
3068
3069   for (y = 0; y < level->fieldy; y++)
3070     for (x = 0; x < level->fieldx; x++)
3071       level->field[x][y] =
3072         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3073                          getFile8Bit(file));
3074   return chunk_size;
3075 }
3076
3077 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3078 {
3079   int i, x, y;
3080   int header_size = 4;
3081   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3082   int chunk_size_expected = header_size + content_size;
3083
3084   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3085      stored with 16-bit encoding (and should be twice as big then).
3086      Even worse, playfield data was stored 16-bit when only yamyam content
3087      contained 16-bit elements and vice versa. */
3088
3089   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3090     chunk_size_expected += content_size;
3091
3092   if (chunk_size_expected != chunk_size)
3093   {
3094     ReadUnusedBytesFromFile(file, chunk_size);
3095     return chunk_size_expected;
3096   }
3097
3098   getFile8Bit(file);
3099   level->num_yamyam_contents = getFile8Bit(file);
3100   getFile8Bit(file);
3101   getFile8Bit(file);
3102
3103   // correct invalid number of content fields -- should never happen
3104   if (level->num_yamyam_contents < 1 ||
3105       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3106     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3107
3108   for (i = 0; i < MAX_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] =
3112           getMappedElement(level->encoding_16bit_field ?
3113                            getFile16BitBE(file) : getFile8Bit(file));
3114   return chunk_size;
3115 }
3116
3117 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3118 {
3119   int i, x, y;
3120   int element;
3121   int num_contents;
3122   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3123
3124   element = getMappedElement(getFile16BitBE(file));
3125   num_contents = getFile8Bit(file);
3126
3127   getFile8Bit(file);    // content x size (unused)
3128   getFile8Bit(file);    // content y size (unused)
3129
3130   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3131
3132   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3133     for (y = 0; y < 3; y++)
3134       for (x = 0; x < 3; x++)
3135         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3136
3137   // correct invalid number of content fields -- should never happen
3138   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3139     num_contents = STD_ELEMENT_CONTENTS;
3140
3141   if (element == EL_YAMYAM)
3142   {
3143     level->num_yamyam_contents = num_contents;
3144
3145     for (i = 0; i < num_contents; i++)
3146       for (y = 0; y < 3; y++)
3147         for (x = 0; x < 3; x++)
3148           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3149   }
3150   else if (element == EL_BD_AMOEBA)
3151   {
3152     level->amoeba_content = content_array[0][0][0];
3153   }
3154   else
3155   {
3156     Warn("cannot load content for element '%d'", element);
3157   }
3158
3159   return chunk_size;
3160 }
3161
3162 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3163 {
3164   int i;
3165   int element;
3166   int envelope_nr;
3167   int envelope_len;
3168   int chunk_size_expected;
3169
3170   element = getMappedElement(getFile16BitBE(file));
3171   if (!IS_ENVELOPE(element))
3172     element = EL_ENVELOPE_1;
3173
3174   envelope_nr = element - EL_ENVELOPE_1;
3175
3176   envelope_len = getFile16BitBE(file);
3177
3178   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3179   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3180
3181   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3182
3183   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3184   if (chunk_size_expected != chunk_size)
3185   {
3186     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3187     return chunk_size_expected;
3188   }
3189
3190   for (i = 0; i < envelope_len; i++)
3191     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3192
3193   return chunk_size;
3194 }
3195
3196 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3197 {
3198   int num_changed_custom_elements = getFile16BitBE(file);
3199   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3200   int i;
3201
3202   if (chunk_size_expected != chunk_size)
3203   {
3204     ReadUnusedBytesFromFile(file, chunk_size - 2);
3205     return chunk_size_expected;
3206   }
3207
3208   for (i = 0; i < num_changed_custom_elements; i++)
3209   {
3210     int element = getMappedElement(getFile16BitBE(file));
3211     int properties = getFile32BitBE(file);
3212
3213     if (IS_CUSTOM_ELEMENT(element))
3214       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3215     else
3216       Warn("invalid custom element number %d", element);
3217
3218     // older game versions that wrote level files with CUS1 chunks used
3219     // different default push delay values (not yet stored in level file)
3220     element_info[element].push_delay_fixed = 2;
3221     element_info[element].push_delay_random = 8;
3222   }
3223
3224   level->file_has_custom_elements = TRUE;
3225
3226   return chunk_size;
3227 }
3228
3229 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3230 {
3231   int num_changed_custom_elements = getFile16BitBE(file);
3232   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3233   int i;
3234
3235   if (chunk_size_expected != chunk_size)
3236   {
3237     ReadUnusedBytesFromFile(file, chunk_size - 2);
3238     return chunk_size_expected;
3239   }
3240
3241   for (i = 0; i < num_changed_custom_elements; i++)
3242   {
3243     int element = getMappedElement(getFile16BitBE(file));
3244     int custom_target_element = getMappedElement(getFile16BitBE(file));
3245
3246     if (IS_CUSTOM_ELEMENT(element))
3247       element_info[element].change->target_element = custom_target_element;
3248     else
3249       Warn("invalid custom element number %d", element);
3250   }
3251
3252   level->file_has_custom_elements = TRUE;
3253
3254   return chunk_size;
3255 }
3256
3257 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3258 {
3259   int num_changed_custom_elements = getFile16BitBE(file);
3260   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3261   int i, j, x, y;
3262
3263   if (chunk_size_expected != chunk_size)
3264   {
3265     ReadUnusedBytesFromFile(file, chunk_size - 2);
3266     return chunk_size_expected;
3267   }
3268
3269   for (i = 0; i < num_changed_custom_elements; i++)
3270   {
3271     int element = getMappedElement(getFile16BitBE(file));
3272     struct ElementInfo *ei = &element_info[element];
3273     unsigned int event_bits;
3274
3275     if (!IS_CUSTOM_ELEMENT(element))
3276     {
3277       Warn("invalid custom element number %d", element);
3278
3279       element = EL_INTERNAL_DUMMY;
3280     }
3281
3282     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3283       ei->description[j] = getFile8Bit(file);
3284     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3285
3286     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3287
3288     // some free bytes for future properties and padding
3289     ReadUnusedBytesFromFile(file, 7);
3290
3291     ei->use_gfx_element = getFile8Bit(file);
3292     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3293
3294     ei->collect_score_initial = getFile8Bit(file);
3295     ei->collect_count_initial = getFile8Bit(file);
3296
3297     ei->push_delay_fixed = getFile16BitBE(file);
3298     ei->push_delay_random = getFile16BitBE(file);
3299     ei->move_delay_fixed = getFile16BitBE(file);
3300     ei->move_delay_random = getFile16BitBE(file);
3301
3302     ei->move_pattern = getFile16BitBE(file);
3303     ei->move_direction_initial = getFile8Bit(file);
3304     ei->move_stepsize = getFile8Bit(file);
3305
3306     for (y = 0; y < 3; y++)
3307       for (x = 0; x < 3; x++)
3308         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3309
3310     // bits 0 - 31 of "has_event[]"
3311     event_bits = getFile32BitBE(file);
3312     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3313       if (event_bits & (1u << j))
3314         ei->change->has_event[j] = TRUE;
3315
3316     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3317
3318     ei->change->delay_fixed = getFile16BitBE(file);
3319     ei->change->delay_random = getFile16BitBE(file);
3320     ei->change->delay_frames = getFile16BitBE(file);
3321
3322     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3323
3324     ei->change->explode = getFile8Bit(file);
3325     ei->change->use_target_content = getFile8Bit(file);
3326     ei->change->only_if_complete = getFile8Bit(file);
3327     ei->change->use_random_replace = getFile8Bit(file);
3328
3329     ei->change->random_percentage = getFile8Bit(file);
3330     ei->change->replace_when = getFile8Bit(file);
3331
3332     for (y = 0; y < 3; y++)
3333       for (x = 0; x < 3; x++)
3334         ei->change->target_content.e[x][y] =
3335           getMappedElement(getFile16BitBE(file));
3336
3337     ei->slippery_type = getFile8Bit(file);
3338
3339     // some free bytes for future properties and padding
3340     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3341
3342     // mark that this custom element has been modified
3343     ei->modified_settings = TRUE;
3344   }
3345
3346   level->file_has_custom_elements = TRUE;
3347
3348   return chunk_size;
3349 }
3350
3351 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3352 {
3353   struct ElementInfo *ei;
3354   int chunk_size_expected;
3355   int element;
3356   int i, j, x, y;
3357
3358   // ---------- custom element base property values (96 bytes) ----------------
3359
3360   element = getMappedElement(getFile16BitBE(file));
3361
3362   if (!IS_CUSTOM_ELEMENT(element))
3363   {
3364     Warn("invalid custom element number %d", element);
3365
3366     ReadUnusedBytesFromFile(file, chunk_size - 2);
3367
3368     return chunk_size;
3369   }
3370
3371   ei = &element_info[element];
3372
3373   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3374     ei->description[i] = getFile8Bit(file);
3375   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3376
3377   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3378
3379   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3380
3381   ei->num_change_pages = getFile8Bit(file);
3382
3383   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3384   if (chunk_size_expected != chunk_size)
3385   {
3386     ReadUnusedBytesFromFile(file, chunk_size - 43);
3387     return chunk_size_expected;
3388   }
3389
3390   ei->ce_value_fixed_initial = getFile16BitBE(file);
3391   ei->ce_value_random_initial = getFile16BitBE(file);
3392   ei->use_last_ce_value = getFile8Bit(file);
3393
3394   ei->use_gfx_element = getFile8Bit(file);
3395   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3396
3397   ei->collect_score_initial = getFile8Bit(file);
3398   ei->collect_count_initial = getFile8Bit(file);
3399
3400   ei->drop_delay_fixed = getFile8Bit(file);
3401   ei->push_delay_fixed = getFile8Bit(file);
3402   ei->drop_delay_random = getFile8Bit(file);
3403   ei->push_delay_random = getFile8Bit(file);
3404   ei->move_delay_fixed = getFile16BitBE(file);
3405   ei->move_delay_random = getFile16BitBE(file);
3406
3407   // bits 0 - 15 of "move_pattern" ...
3408   ei->move_pattern = getFile16BitBE(file);
3409   ei->move_direction_initial = getFile8Bit(file);
3410   ei->move_stepsize = getFile8Bit(file);
3411
3412   ei->slippery_type = getFile8Bit(file);
3413
3414   for (y = 0; y < 3; y++)
3415     for (x = 0; x < 3; x++)
3416       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3417
3418   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3419   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3420   ei->move_leave_type = getFile8Bit(file);
3421
3422   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3423   ei->move_pattern |= (getFile16BitBE(file) << 16);
3424
3425   ei->access_direction = getFile8Bit(file);
3426
3427   ei->explosion_delay = getFile8Bit(file);
3428   ei->ignition_delay = getFile8Bit(file);
3429   ei->explosion_type = getFile8Bit(file);
3430
3431   // some free bytes for future custom property values and padding
3432   ReadUnusedBytesFromFile(file, 1);
3433
3434   // ---------- change page property values (48 bytes) ------------------------
3435
3436   setElementChangePages(ei, ei->num_change_pages);
3437
3438   for (i = 0; i < ei->num_change_pages; i++)
3439   {
3440     struct ElementChangeInfo *change = &ei->change_page[i];
3441     unsigned int event_bits;
3442
3443     // always start with reliable default values
3444     setElementChangeInfoToDefaults(change);
3445
3446     // bits 0 - 31 of "has_event[]" ...
3447     event_bits = getFile32BitBE(file);
3448     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3449       if (event_bits & (1u << j))
3450         change->has_event[j] = TRUE;
3451
3452     change->target_element = getMappedElement(getFile16BitBE(file));
3453
3454     change->delay_fixed = getFile16BitBE(file);
3455     change->delay_random = getFile16BitBE(file);
3456     change->delay_frames = getFile16BitBE(file);
3457
3458     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3459
3460     change->explode = getFile8Bit(file);
3461     change->use_target_content = getFile8Bit(file);
3462     change->only_if_complete = getFile8Bit(file);
3463     change->use_random_replace = getFile8Bit(file);
3464
3465     change->random_percentage = getFile8Bit(file);
3466     change->replace_when = getFile8Bit(file);
3467
3468     for (y = 0; y < 3; y++)
3469       for (x = 0; x < 3; x++)
3470         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3471
3472     change->can_change = getFile8Bit(file);
3473
3474     change->trigger_side = getFile8Bit(file);
3475
3476     change->trigger_player = getFile8Bit(file);
3477     change->trigger_page = getFile8Bit(file);
3478
3479     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3480                             CH_PAGE_ANY : (1 << change->trigger_page));
3481
3482     change->has_action = getFile8Bit(file);
3483     change->action_type = getFile8Bit(file);
3484     change->action_mode = getFile8Bit(file);
3485     change->action_arg = getFile16BitBE(file);
3486
3487     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3488     event_bits = getFile8Bit(file);
3489     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3490       if (event_bits & (1u << (j - 32)))
3491         change->has_event[j] = TRUE;
3492   }
3493
3494   // mark this custom element as modified
3495   ei->modified_settings = TRUE;
3496
3497   level->file_has_custom_elements = TRUE;
3498
3499   return chunk_size;
3500 }
3501
3502 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3503 {
3504   struct ElementInfo *ei;
3505   struct ElementGroupInfo *group;
3506   int element;
3507   int i;
3508
3509   element = getMappedElement(getFile16BitBE(file));
3510
3511   if (!IS_GROUP_ELEMENT(element))
3512   {
3513     Warn("invalid group element number %d", element);
3514
3515     ReadUnusedBytesFromFile(file, chunk_size - 2);
3516
3517     return chunk_size;
3518   }
3519
3520   ei = &element_info[element];
3521
3522   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3523     ei->description[i] = getFile8Bit(file);
3524   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3525
3526   group = element_info[element].group;
3527
3528   group->num_elements = getFile8Bit(file);
3529
3530   ei->use_gfx_element = getFile8Bit(file);
3531   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3532
3533   group->choice_mode = getFile8Bit(file);
3534
3535   // some free bytes for future values and padding
3536   ReadUnusedBytesFromFile(file, 3);
3537
3538   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3539     group->element[i] = getMappedElement(getFile16BitBE(file));
3540
3541   // mark this group element as modified
3542   element_info[element].modified_settings = TRUE;
3543
3544   level->file_has_custom_elements = TRUE;
3545
3546   return chunk_size;
3547 }
3548
3549 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3550                                 int element, int real_element)
3551 {
3552   int micro_chunk_size = 0;
3553   int conf_type = getFile8Bit(file);
3554   int byte_mask = conf_type & CONF_MASK_BYTES;
3555   boolean element_found = FALSE;
3556   int i;
3557
3558   micro_chunk_size += 1;
3559
3560   if (byte_mask == CONF_MASK_MULTI_BYTES)
3561   {
3562     int num_bytes = getFile16BitBE(file);
3563     byte *buffer = checked_malloc(num_bytes);
3564
3565     ReadBytesFromFile(file, buffer, num_bytes);
3566
3567     for (i = 0; conf[i].data_type != -1; i++)
3568     {
3569       if (conf[i].element == element &&
3570           conf[i].conf_type == conf_type)
3571       {
3572         int data_type = conf[i].data_type;
3573         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3574         int max_num_entities = conf[i].max_num_entities;
3575
3576         if (num_entities > max_num_entities)
3577         {
3578           Warn("truncating number of entities for element %d from %d to %d",
3579                element, num_entities, max_num_entities);
3580
3581           num_entities = max_num_entities;
3582         }
3583
3584         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3585                                   data_type == TYPE_CONTENT_LIST))
3586         {
3587           // for element and content lists, zero entities are not allowed
3588           Warn("found empty list of entities for element %d", element);
3589
3590           // do not set "num_entities" here to prevent reading behind buffer
3591
3592           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3593         }
3594         else
3595         {
3596           *(int *)(conf[i].num_entities) = num_entities;
3597         }
3598
3599         element_found = TRUE;
3600
3601         if (data_type == TYPE_STRING)
3602         {
3603           char *string = (char *)(conf[i].value);
3604           int j;
3605
3606           for (j = 0; j < max_num_entities; j++)
3607             string[j] = (j < num_entities ? buffer[j] : '\0');
3608         }
3609         else if (data_type == TYPE_ELEMENT_LIST)
3610         {
3611           int *element_array = (int *)(conf[i].value);
3612           int j;
3613
3614           for (j = 0; j < num_entities; j++)
3615             element_array[j] =
3616               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3617         }
3618         else if (data_type == TYPE_CONTENT_LIST)
3619         {
3620           struct Content *content= (struct Content *)(conf[i].value);
3621           int c, x, y;
3622
3623           for (c = 0; c < num_entities; c++)
3624             for (y = 0; y < 3; y++)
3625               for (x = 0; x < 3; x++)
3626                 content[c].e[x][y] =
3627                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3628         }
3629         else
3630           element_found = FALSE;
3631
3632         break;
3633       }
3634     }
3635
3636     checked_free(buffer);
3637
3638     micro_chunk_size += 2 + num_bytes;
3639   }
3640   else          // constant size configuration data (1, 2 or 4 bytes)
3641   {
3642     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3643                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3644                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3645
3646     for (i = 0; conf[i].data_type != -1; i++)
3647     {
3648       if (conf[i].element == element &&
3649           conf[i].conf_type == conf_type)
3650       {
3651         int data_type = conf[i].data_type;
3652
3653         if (data_type == TYPE_ELEMENT)
3654           value = getMappedElement(value);
3655
3656         if (data_type == TYPE_BOOLEAN)
3657           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3658         else
3659           *(int *)    (conf[i].value) = value;
3660
3661         element_found = TRUE;
3662
3663         break;
3664       }
3665     }
3666
3667     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3668   }
3669
3670   if (!element_found)
3671   {
3672     char *error_conf_chunk_bytes =
3673       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3674        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3675        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3676     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3677     int error_element = real_element;
3678
3679     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3680          error_conf_chunk_bytes, error_conf_chunk_token,
3681          error_element, EL_NAME(error_element));
3682   }
3683
3684   return micro_chunk_size;
3685 }
3686
3687 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3688 {
3689   int real_chunk_size = 0;
3690
3691   li = *level;          // copy level data into temporary buffer
3692
3693   while (!checkEndOfFile(file))
3694   {
3695     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3696
3697     if (real_chunk_size >= chunk_size)
3698       break;
3699   }
3700
3701   *level = li;          // copy temporary buffer back to level data
3702
3703   return real_chunk_size;
3704 }
3705
3706 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3707 {
3708   int real_chunk_size = 0;
3709
3710   li = *level;          // copy level data into temporary buffer
3711
3712   while (!checkEndOfFile(file))
3713   {
3714     int element = getMappedElement(getFile16BitBE(file));
3715
3716     real_chunk_size += 2;
3717     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3718                                             element, element);
3719     if (real_chunk_size >= chunk_size)
3720       break;
3721   }
3722
3723   *level = li;          // copy temporary buffer back to level data
3724
3725   return real_chunk_size;
3726 }
3727
3728 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3729 {
3730   int real_chunk_size = 0;
3731
3732   li = *level;          // copy level data into temporary buffer
3733
3734   while (!checkEndOfFile(file))
3735   {
3736     int element = getMappedElement(getFile16BitBE(file));
3737
3738     real_chunk_size += 2;
3739     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3740                                             element, element);
3741     if (real_chunk_size >= chunk_size)
3742       break;
3743   }
3744
3745   *level = li;          // copy temporary buffer back to level data
3746
3747   return real_chunk_size;
3748 }
3749
3750 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3751 {
3752   int element = getMappedElement(getFile16BitBE(file));
3753   int envelope_nr = element - EL_ENVELOPE_1;
3754   int real_chunk_size = 2;
3755
3756   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3757
3758   while (!checkEndOfFile(file))
3759   {
3760     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3761                                             -1, element);
3762
3763     if (real_chunk_size >= chunk_size)
3764       break;
3765   }
3766
3767   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3768
3769   return real_chunk_size;
3770 }
3771
3772 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3773 {
3774   int element = getMappedElement(getFile16BitBE(file));
3775   int real_chunk_size = 2;
3776   struct ElementInfo *ei = &element_info[element];
3777   int i;
3778
3779   xx_ei = *ei;          // copy element data into temporary buffer
3780
3781   xx_ei.num_change_pages = -1;
3782
3783   while (!checkEndOfFile(file))
3784   {
3785     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3786                                             -1, element);
3787     if (xx_ei.num_change_pages != -1)
3788       break;
3789
3790     if (real_chunk_size >= chunk_size)
3791       break;
3792   }
3793
3794   *ei = xx_ei;
3795
3796   if (ei->num_change_pages == -1)
3797   {
3798     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3799          EL_NAME(element));
3800
3801     ei->num_change_pages = 1;
3802
3803     setElementChangePages(ei, 1);
3804     setElementChangeInfoToDefaults(ei->change);
3805
3806     return real_chunk_size;
3807   }
3808
3809   // initialize number of change pages stored for this custom element
3810   setElementChangePages(ei, ei->num_change_pages);
3811   for (i = 0; i < ei->num_change_pages; i++)
3812     setElementChangeInfoToDefaults(&ei->change_page[i]);
3813
3814   // start with reading properties for the first change page
3815   xx_current_change_page = 0;
3816
3817   while (!checkEndOfFile(file))
3818   {
3819     // level file might contain invalid change page number
3820     if (xx_current_change_page >= ei->num_change_pages)
3821       break;
3822
3823     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3824
3825     xx_change = *change;        // copy change data into temporary buffer
3826
3827     resetEventBits();           // reset bits; change page might have changed
3828
3829     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3830                                             -1, element);
3831
3832     *change = xx_change;
3833
3834     setEventFlagsFromEventBits(change);
3835
3836     if (real_chunk_size >= chunk_size)
3837       break;
3838   }
3839
3840   level->file_has_custom_elements = TRUE;
3841
3842   return real_chunk_size;
3843 }
3844
3845 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3846 {
3847   int element = getMappedElement(getFile16BitBE(file));
3848   int real_chunk_size = 2;
3849   struct ElementInfo *ei = &element_info[element];
3850   struct ElementGroupInfo *group = ei->group;
3851
3852   if (group == NULL)
3853     return -1;
3854
3855   xx_ei = *ei;          // copy element data into temporary buffer
3856   xx_group = *group;    // copy group data into temporary buffer
3857
3858   while (!checkEndOfFile(file))
3859   {
3860     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3861                                             -1, element);
3862
3863     if (real_chunk_size >= chunk_size)
3864       break;
3865   }
3866
3867   *ei = xx_ei;
3868   *group = xx_group;
3869
3870   level->file_has_custom_elements = TRUE;
3871
3872   return real_chunk_size;
3873 }
3874
3875 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3876 {
3877   int element = getMappedElement(getFile16BitBE(file));
3878   int real_chunk_size = 2;
3879   struct ElementInfo *ei = &element_info[element];
3880
3881   xx_ei = *ei;          // copy element data into temporary buffer
3882
3883   while (!checkEndOfFile(file))
3884   {
3885     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3886                                             -1, element);
3887
3888     if (real_chunk_size >= chunk_size)
3889       break;
3890   }
3891
3892   *ei = xx_ei;
3893
3894   level->file_has_custom_elements = TRUE;
3895
3896   return real_chunk_size;
3897 }
3898
3899 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3900                                       struct LevelFileInfo *level_file_info,
3901                                       boolean level_info_only)
3902 {
3903   char *filename = level_file_info->filename;
3904   char cookie[MAX_LINE_LEN];
3905   char chunk_name[CHUNK_ID_LEN + 1];
3906   int chunk_size;
3907   File *file;
3908
3909   if (!(file = openFile(filename, MODE_READ)))
3910   {
3911     level->no_valid_file = TRUE;
3912     level->no_level_file = TRUE;
3913
3914     if (level_info_only)
3915       return;
3916
3917     Warn("cannot read level '%s' -- using empty level", filename);
3918
3919     if (!setup.editor.use_template_for_new_levels)
3920       return;
3921
3922     // if level file not found, try to initialize level data from template
3923     filename = getGlobalLevelTemplateFilename();
3924
3925     if (!(file = openFile(filename, MODE_READ)))
3926       return;
3927
3928     // default: for empty levels, use level template for custom elements
3929     level->use_custom_template = TRUE;
3930
3931     level->no_valid_file = FALSE;
3932   }
3933
3934   getFileChunkBE(file, chunk_name, NULL);
3935   if (strEqual(chunk_name, "RND1"))
3936   {
3937     getFile32BitBE(file);               // not used
3938
3939     getFileChunkBE(file, chunk_name, NULL);
3940     if (!strEqual(chunk_name, "CAVE"))
3941     {
3942       level->no_valid_file = TRUE;
3943
3944       Warn("unknown format of level file '%s'", filename);
3945
3946       closeFile(file);
3947
3948       return;
3949     }
3950   }
3951   else  // check for pre-2.0 file format with cookie string
3952   {
3953     strcpy(cookie, chunk_name);
3954     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3955       cookie[4] = '\0';
3956     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3957       cookie[strlen(cookie) - 1] = '\0';
3958
3959     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3960     {
3961       level->no_valid_file = TRUE;
3962
3963       Warn("unknown format of level file '%s'", filename);
3964
3965       closeFile(file);
3966
3967       return;
3968     }
3969
3970     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3971     {
3972       level->no_valid_file = TRUE;
3973
3974       Warn("unsupported version of level file '%s'", filename);
3975
3976       closeFile(file);
3977
3978       return;
3979     }
3980
3981     // pre-2.0 level files have no game version, so use file version here
3982     level->game_version = level->file_version;
3983   }
3984
3985   if (level->file_version < FILE_VERSION_1_2)
3986   {
3987     // level files from versions before 1.2.0 without chunk structure
3988     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3989     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3990   }
3991   else
3992   {
3993     static struct
3994     {
3995       char *name;
3996       int size;
3997       int (*loader)(File *, int, struct LevelInfo *);
3998     }
3999     chunk_info[] =
4000     {
4001       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4002       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4003       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4004       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4005       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4006       { "INFO", -1,                     LoadLevel_INFO },
4007       { "BODY", -1,                     LoadLevel_BODY },
4008       { "CONT", -1,                     LoadLevel_CONT },
4009       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4010       { "CNT3", -1,                     LoadLevel_CNT3 },
4011       { "CUS1", -1,                     LoadLevel_CUS1 },
4012       { "CUS2", -1,                     LoadLevel_CUS2 },
4013       { "CUS3", -1,                     LoadLevel_CUS3 },
4014       { "CUS4", -1,                     LoadLevel_CUS4 },
4015       { "GRP1", -1,                     LoadLevel_GRP1 },
4016       { "CONF", -1,                     LoadLevel_CONF },
4017       { "ELEM", -1,                     LoadLevel_ELEM },
4018       { "NOTE", -1,                     LoadLevel_NOTE },
4019       { "CUSX", -1,                     LoadLevel_CUSX },
4020       { "GRPX", -1,                     LoadLevel_GRPX },
4021       { "EMPX", -1,                     LoadLevel_EMPX },
4022
4023       {  NULL,  0,                      NULL }
4024     };
4025
4026     while (getFileChunkBE(file, chunk_name, &chunk_size))
4027     {
4028       int i = 0;
4029
4030       while (chunk_info[i].name != NULL &&
4031              !strEqual(chunk_name, chunk_info[i].name))
4032         i++;
4033
4034       if (chunk_info[i].name == NULL)
4035       {
4036         Warn("unknown chunk '%s' in level file '%s'",
4037              chunk_name, filename);
4038
4039         ReadUnusedBytesFromFile(file, chunk_size);
4040       }
4041       else if (chunk_info[i].size != -1 &&
4042                chunk_info[i].size != chunk_size)
4043       {
4044         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4045              chunk_size, chunk_name, filename);
4046
4047         ReadUnusedBytesFromFile(file, chunk_size);
4048       }
4049       else
4050       {
4051         // call function to load this level chunk
4052         int chunk_size_expected =
4053           (chunk_info[i].loader)(file, chunk_size, level);
4054
4055         if (chunk_size_expected < 0)
4056         {
4057           Warn("error reading chunk '%s' in level file '%s'",
4058                chunk_name, filename);
4059
4060           break;
4061         }
4062
4063         // the size of some chunks cannot be checked before reading other
4064         // chunks first (like "HEAD" and "BODY") that contain some header
4065         // information, so check them here
4066         if (chunk_size_expected != chunk_size)
4067         {
4068           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4069                chunk_size, chunk_name, filename);
4070
4071           break;
4072         }
4073       }
4074     }
4075   }
4076
4077   closeFile(file);
4078 }
4079
4080
4081 // ----------------------------------------------------------------------------
4082 // functions for loading BD level
4083 // ----------------------------------------------------------------------------
4084
4085 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4086 {
4087   struct LevelInfo_BD *level_bd = level->native_bd_level;
4088   GdCave *cave = NULL;  // will be changed below
4089   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4090   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4091   int x, y;
4092
4093   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4094
4095   // cave and map newly allocated when set to defaults above
4096   cave = level_bd->cave;
4097
4098   // level type
4099   cave->intermission                    = level->bd_intermission;
4100
4101   // level settings
4102   cave->level_time[0]                   = level->time;
4103   cave->level_diamonds[0]               = level->gems_needed;
4104
4105   // game timing
4106   cave->scheduling                      = level->bd_scheduling_type;
4107   cave->pal_timing                      = level->bd_pal_timing;
4108   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4109   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4110   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4111   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4112
4113   // scores
4114   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4115   cave->diamond_value                   = level->score[SC_EMERALD];
4116   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4117
4118   // compatibility settings
4119   cave->lineshift                       = level->bd_line_shifting_borders;
4120   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4121   cave->short_explosions                = level->bd_short_explosions;
4122   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4123
4124   // player properties
4125   cave->diagonal_movements              = level->bd_diagonal_movements;
4126   cave->active_is_first_found           = level->bd_topmost_player_active;
4127   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4128   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4129   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4130   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4131
4132   // element properties
4133   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4134   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4135   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4136   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4137   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4138   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4139   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4140   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4141   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4142
4143   cave->magic_diamond_to                = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4144   cave->magic_stone_to                  = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4145   cave->magic_mega_stone_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4146   cave->magic_nut_to                    = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4147   cave->magic_nitro_pack_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4148   cave->magic_flying_diamond_to         = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4149   cave->magic_flying_stone_to           = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4150
4151   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4152   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4153   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4154   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4155   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4156   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4157   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4158   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4159   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4160   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4161   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4162
4163   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4164   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4165   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4166   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4167   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4168   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4169
4170   cave->slime_predictable               = level->bd_slime_is_predictable;
4171   cave->slime_correct_random            = level->bd_slime_correct_random;
4172   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4173   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4174   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4175   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4176   cave->slime_eats_1                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4177   cave->slime_converts_1                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4178   cave->slime_eats_2                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4179   cave->slime_converts_2                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4180   cave->slime_eats_3                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4181   cave->slime_converts_3                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4182
4183   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4184   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4185   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4186
4187   cave->biter_delay_frame               = level->bd_biter_move_delay;
4188   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4189
4190   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4191
4192   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4193
4194   cave->replicators_active              = level->bd_replicators_active;
4195   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4196
4197   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4198   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4199
4200   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4201
4202   cave->nut_turns_to_when_crushed       = map_element_RND_to_BD_cave(level->bd_nut_content);
4203
4204   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4205   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4206   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4207
4208   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4209   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4210
4211   cave->expanding_wall_looks_like       = map_element_RND_to_BD_cave(level->bd_expanding_wall_looks_like);
4212
4213   // level name
4214   strncpy(cave->name, level->name, sizeof(GdString));
4215   cave->name[sizeof(GdString) - 1] = '\0';
4216
4217   // playfield elements
4218   for (x = 0; x < cave->w; x++)
4219     for (y = 0; y < cave->h; y++)
4220       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4221 }
4222
4223 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4224 {
4225   struct LevelInfo_BD *level_bd = level->native_bd_level;
4226   GdCave *cave = level_bd->cave;
4227   int bd_level_nr = level_bd->level_nr;
4228   int x, y;
4229
4230   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4231   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4232
4233   // level type
4234   level->bd_intermission                = cave->intermission;
4235
4236   // level settings
4237   level->time                           = cave->level_time[bd_level_nr];
4238   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4239
4240   // game timing
4241   level->bd_scheduling_type             = cave->scheduling;
4242   level->bd_pal_timing                  = cave->pal_timing;
4243   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4244   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4245   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4246   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4247
4248   // scores
4249   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4250   level->score[SC_EMERALD]              = cave->diamond_value;
4251   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4252
4253   // compatibility settings
4254   level->bd_line_shifting_borders       = cave->lineshift;
4255   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4256   level->bd_short_explosions            = cave->short_explosions;
4257   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4258
4259   // player properties
4260   level->bd_diagonal_movements          = cave->diagonal_movements;
4261   level->bd_topmost_player_active       = cave->active_is_first_found;
4262   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4263   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4264   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4265   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4266
4267   // element properties
4268   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4269   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4270   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4271   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4272   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4273   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4274   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4275   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4276   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4277
4278   level->bd_magic_wall_diamond_to       = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4279   level->bd_magic_wall_rock_to          = map_element_BD_to_RND_cave(cave->magic_stone_to);
4280   level->bd_magic_wall_mega_rock_to     = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4281   level->bd_magic_wall_nut_to           = map_element_BD_to_RND_cave(cave->magic_nut_to);
4282   level->bd_magic_wall_nitro_pack_to    = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4283   level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4284   level->bd_magic_wall_flying_rock_to   = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4285
4286   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4287   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4288   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4289   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4290   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4291   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4292   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4293   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4294   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4295   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4296   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4297
4298   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4299   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4300   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4301   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4302   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4303   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4304
4305   level->bd_slime_is_predictable        = cave->slime_predictable;
4306   level->bd_slime_correct_random        = cave->slime_correct_random;
4307   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4308   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4309   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4310   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4311   level->bd_slime_eats_element_1        = map_element_BD_to_RND_cave(cave->slime_eats_1);
4312   level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4313   level->bd_slime_eats_element_2        = map_element_BD_to_RND_cave(cave->slime_eats_2);
4314   level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4315   level->bd_slime_eats_element_3        = map_element_BD_to_RND_cave(cave->slime_eats_3);
4316   level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4317
4318   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4319   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4320   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4321
4322   level->bd_biter_move_delay            = cave->biter_delay_frame;
4323   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4324
4325   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4326
4327   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4328
4329   level->bd_replicators_active          = cave->replicators_active;
4330   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4331
4332   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4333   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4334
4335   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4336
4337   level->bd_nut_content                 = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4338
4339   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4340   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4341   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4342
4343   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4344   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4345
4346   level->bd_expanding_wall_looks_like   = map_element_BD_to_RND_cave(cave->expanding_wall_looks_like);
4347
4348   // level name
4349   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4350
4351   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4352   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4353
4354   // playfield elements
4355   for (x = 0; x < level->fieldx; x++)
4356     for (y = 0; y < level->fieldy; y++)
4357       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4358
4359   checked_free(cave_name);
4360 }
4361
4362 static void setTapeInfoToDefaults(void);
4363
4364 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4365 {
4366   struct LevelInfo_BD *level_bd = level->native_bd_level;
4367   GdCave *cave = level_bd->cave;
4368   GdReplay *replay = level_bd->replay;
4369   int i;
4370
4371   if (replay == NULL)
4372     return;
4373
4374   // always start with reliable default values
4375   setTapeInfoToDefaults();
4376
4377   tape.level_nr = level_nr;             // (currently not used)
4378   tape.random_seed = replay->seed;
4379
4380   TapeSetDateFromIsoDateString(replay->date);
4381
4382   tape.counter = 0;
4383   tape.pos[tape.counter].delay = 0;
4384
4385   tape.bd_replay = TRUE;
4386
4387   // all time calculations only used to display approximate tape time
4388   int cave_speed = cave->speed;
4389   int milliseconds_game = 0;
4390   int milliseconds_elapsed = 20;
4391
4392   for (i = 0; i < replay->movements->len; i++)
4393   {
4394     int replay_action = replay->movements->data[i];
4395     int tape_action = map_action_BD_to_RND(replay_action);
4396     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4397     boolean success = 0;
4398
4399     while (1)
4400     {
4401       success = TapeAddAction(action);
4402
4403       milliseconds_game += milliseconds_elapsed;
4404
4405       if (milliseconds_game >= cave_speed)
4406       {
4407         milliseconds_game -= cave_speed;
4408
4409         break;
4410       }
4411     }
4412
4413     tape.counter++;
4414     tape.pos[tape.counter].delay = 0;
4415     tape.pos[tape.counter].action[0] = 0;
4416
4417     if (!success)
4418     {
4419       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4420
4421       break;
4422     }
4423   }
4424
4425   TapeHaltRecording();
4426 }
4427
4428
4429 // ----------------------------------------------------------------------------
4430 // functions for loading EM level
4431 // ----------------------------------------------------------------------------
4432
4433 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4434 {
4435   static int ball_xy[8][2] =
4436   {
4437     { 0, 0 },
4438     { 1, 0 },
4439     { 2, 0 },
4440     { 0, 1 },
4441     { 2, 1 },
4442     { 0, 2 },
4443     { 1, 2 },
4444     { 2, 2 },
4445   };
4446   struct LevelInfo_EM *level_em = level->native_em_level;
4447   struct CAVE *cav = level_em->cav;
4448   int i, j, x, y;
4449
4450   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4451   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4452
4453   cav->time_seconds     = level->time;
4454   cav->gems_needed      = level->gems_needed;
4455
4456   cav->emerald_score    = level->score[SC_EMERALD];
4457   cav->diamond_score    = level->score[SC_DIAMOND];
4458   cav->alien_score      = level->score[SC_ROBOT];
4459   cav->tank_score       = level->score[SC_SPACESHIP];
4460   cav->bug_score        = level->score[SC_BUG];
4461   cav->eater_score      = level->score[SC_YAMYAM];
4462   cav->nut_score        = level->score[SC_NUT];
4463   cav->dynamite_score   = level->score[SC_DYNAMITE];
4464   cav->key_score        = level->score[SC_KEY];
4465   cav->exit_score       = level->score[SC_TIME_BONUS];
4466
4467   cav->num_eater_arrays = level->num_yamyam_contents;
4468
4469   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4470     for (y = 0; y < 3; y++)
4471       for (x = 0; x < 3; x++)
4472         cav->eater_array[i][y * 3 + x] =
4473           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4474
4475   cav->amoeba_time              = level->amoeba_speed;
4476   cav->wonderwall_time          = level->time_magic_wall;
4477   cav->wheel_time               = level->time_wheel;
4478
4479   cav->android_move_time        = level->android_move_time;
4480   cav->android_clone_time       = level->android_clone_time;
4481   cav->ball_random              = level->ball_random;
4482   cav->ball_active              = level->ball_active_initial;
4483   cav->ball_time                = level->ball_time;
4484   cav->num_ball_arrays          = level->num_ball_contents;
4485
4486   cav->lenses_score             = level->lenses_score;
4487   cav->magnify_score            = level->magnify_score;
4488   cav->slurp_score              = level->slurp_score;
4489
4490   cav->lenses_time              = level->lenses_time;
4491   cav->magnify_time             = level->magnify_time;
4492
4493   cav->wind_time = 9999;
4494   cav->wind_direction =
4495     map_direction_RND_to_EM(level->wind_direction_initial);
4496
4497   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4498     for (j = 0; j < 8; j++)
4499       cav->ball_array[i][j] =
4500         map_element_RND_to_EM_cave(level->ball_content[i].
4501                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4502
4503   map_android_clone_elements_RND_to_EM(level);
4504
4505   // first fill the complete playfield with the empty space element
4506   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4507     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4508       cav->cave[x][y] = Cblank;
4509
4510   // then copy the real level contents from level file into the playfield
4511   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4512   {
4513     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4514
4515     if (level->field[x][y] == EL_AMOEBA_DEAD)
4516       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4517
4518     cav->cave[x][y] = new_element;
4519   }
4520
4521   for (i = 0; i < MAX_PLAYERS; i++)
4522   {
4523     cav->player_x[i] = -1;
4524     cav->player_y[i] = -1;
4525   }
4526
4527   // initialize player positions and delete players from the playfield
4528   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4529   {
4530     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4531     {
4532       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4533
4534       cav->player_x[player_nr] = x;
4535       cav->player_y[player_nr] = y;
4536
4537       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4538     }
4539   }
4540 }
4541
4542 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4543 {
4544   static int ball_xy[8][2] =
4545   {
4546     { 0, 0 },
4547     { 1, 0 },
4548     { 2, 0 },
4549     { 0, 1 },
4550     { 2, 1 },
4551     { 0, 2 },
4552     { 1, 2 },
4553     { 2, 2 },
4554   };
4555   struct LevelInfo_EM *level_em = level->native_em_level;
4556   struct CAVE *cav = level_em->cav;
4557   int i, j, x, y;
4558
4559   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4560   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4561
4562   level->time        = cav->time_seconds;
4563   level->gems_needed = cav->gems_needed;
4564
4565   sprintf(level->name, "Level %d", level->file_info.nr);
4566
4567   level->score[SC_EMERALD]      = cav->emerald_score;
4568   level->score[SC_DIAMOND]      = cav->diamond_score;
4569   level->score[SC_ROBOT]        = cav->alien_score;
4570   level->score[SC_SPACESHIP]    = cav->tank_score;
4571   level->score[SC_BUG]          = cav->bug_score;
4572   level->score[SC_YAMYAM]       = cav->eater_score;
4573   level->score[SC_NUT]          = cav->nut_score;
4574   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4575   level->score[SC_KEY]          = cav->key_score;
4576   level->score[SC_TIME_BONUS]   = cav->exit_score;
4577
4578   level->num_yamyam_contents    = cav->num_eater_arrays;
4579
4580   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4581     for (y = 0; y < 3; y++)
4582       for (x = 0; x < 3; x++)
4583         level->yamyam_content[i].e[x][y] =
4584           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4585
4586   level->amoeba_speed           = cav->amoeba_time;
4587   level->time_magic_wall        = cav->wonderwall_time;
4588   level->time_wheel             = cav->wheel_time;
4589
4590   level->android_move_time      = cav->android_move_time;
4591   level->android_clone_time     = cav->android_clone_time;
4592   level->ball_random            = cav->ball_random;
4593   level->ball_active_initial    = cav->ball_active;
4594   level->ball_time              = cav->ball_time;
4595   level->num_ball_contents      = cav->num_ball_arrays;
4596
4597   level->lenses_score           = cav->lenses_score;
4598   level->magnify_score          = cav->magnify_score;
4599   level->slurp_score            = cav->slurp_score;
4600
4601   level->lenses_time            = cav->lenses_time;
4602   level->magnify_time           = cav->magnify_time;
4603
4604   level->wind_direction_initial =
4605     map_direction_EM_to_RND(cav->wind_direction);
4606
4607   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4608     for (j = 0; j < 8; j++)
4609       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4610         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4611
4612   map_android_clone_elements_EM_to_RND(level);
4613
4614   // convert the playfield (some elements need special treatment)
4615   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4616   {
4617     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4618
4619     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4620       new_element = EL_AMOEBA_DEAD;
4621
4622     level->field[x][y] = new_element;
4623   }
4624
4625   for (i = 0; i < MAX_PLAYERS; i++)
4626   {
4627     // in case of all players set to the same field, use the first player
4628     int nr = MAX_PLAYERS - i - 1;
4629     int jx = cav->player_x[nr];
4630     int jy = cav->player_y[nr];
4631
4632     if (jx != -1 && jy != -1)
4633       level->field[jx][jy] = EL_PLAYER_1 + nr;
4634   }
4635
4636   // time score is counted for each 10 seconds left in Emerald Mine levels
4637   level->time_score_base = 10;
4638 }
4639
4640
4641 // ----------------------------------------------------------------------------
4642 // functions for loading SP level
4643 // ----------------------------------------------------------------------------
4644
4645 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4646 {
4647   struct LevelInfo_SP *level_sp = level->native_sp_level;
4648   LevelInfoType *header = &level_sp->header;
4649   int i, x, y;
4650
4651   level_sp->width  = level->fieldx;
4652   level_sp->height = level->fieldy;
4653
4654   for (x = 0; x < level->fieldx; x++)
4655     for (y = 0; y < level->fieldy; y++)
4656       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4657
4658   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4659
4660   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4661     header->LevelTitle[i] = level->name[i];
4662   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4663
4664   header->InfotronsNeeded = level->gems_needed;
4665
4666   header->SpecialPortCount = 0;
4667
4668   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4669   {
4670     boolean gravity_port_found = FALSE;
4671     boolean gravity_port_valid = FALSE;
4672     int gravity_port_flag;
4673     int gravity_port_base_element;
4674     int element = level->field[x][y];
4675
4676     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4677         element <= EL_SP_GRAVITY_ON_PORT_UP)
4678     {
4679       gravity_port_found = TRUE;
4680       gravity_port_valid = TRUE;
4681       gravity_port_flag = 1;
4682       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4683     }
4684     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4685              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4686     {
4687       gravity_port_found = TRUE;
4688       gravity_port_valid = TRUE;
4689       gravity_port_flag = 0;
4690       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4691     }
4692     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4693              element <= EL_SP_GRAVITY_PORT_UP)
4694     {
4695       // change R'n'D style gravity inverting special port to normal port
4696       // (there are no gravity inverting ports in native Supaplex engine)
4697
4698       gravity_port_found = TRUE;
4699       gravity_port_valid = FALSE;
4700       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4701     }
4702
4703     if (gravity_port_found)
4704     {
4705       if (gravity_port_valid &&
4706           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4707       {
4708         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4709
4710         port->PortLocation = (y * level->fieldx + x) * 2;
4711         port->Gravity = gravity_port_flag;
4712
4713         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4714
4715         header->SpecialPortCount++;
4716       }
4717       else
4718       {
4719         // change special gravity port to normal port
4720
4721         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4722       }
4723
4724       level_sp->playfield[x][y] = element - EL_SP_START;
4725     }
4726   }
4727 }
4728
4729 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4730 {
4731   struct LevelInfo_SP *level_sp = level->native_sp_level;
4732   LevelInfoType *header = &level_sp->header;
4733   boolean num_invalid_elements = 0;
4734   int i, j, x, y;
4735
4736   level->fieldx = level_sp->width;
4737   level->fieldy = level_sp->height;
4738
4739   for (x = 0; x < level->fieldx; x++)
4740   {
4741     for (y = 0; y < level->fieldy; y++)
4742     {
4743       int element_old = level_sp->playfield[x][y];
4744       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4745
4746       if (element_new == EL_UNKNOWN)
4747       {
4748         num_invalid_elements++;
4749
4750         Debug("level:native:SP", "invalid element %d at position %d, %d",
4751               element_old, x, y);
4752       }
4753
4754       level->field[x][y] = element_new;
4755     }
4756   }
4757
4758   if (num_invalid_elements > 0)
4759     Warn("found %d invalid elements%s", num_invalid_elements,
4760          (!options.debug ? " (use '--debug' for more details)" : ""));
4761
4762   for (i = 0; i < MAX_PLAYERS; i++)
4763     level->initial_player_gravity[i] =
4764       (header->InitialGravity == 1 ? TRUE : FALSE);
4765
4766   // skip leading spaces
4767   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4768     if (header->LevelTitle[i] != ' ')
4769       break;
4770
4771   // copy level title
4772   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4773     level->name[j] = header->LevelTitle[i];
4774   level->name[j] = '\0';
4775
4776   // cut trailing spaces
4777   for (; j > 0; j--)
4778     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4779       level->name[j - 1] = '\0';
4780
4781   level->gems_needed = header->InfotronsNeeded;
4782
4783   for (i = 0; i < header->SpecialPortCount; i++)
4784   {
4785     SpecialPortType *port = &header->SpecialPort[i];
4786     int port_location = port->PortLocation;
4787     int gravity = port->Gravity;
4788     int port_x, port_y, port_element;
4789
4790     port_x = (port_location / 2) % level->fieldx;
4791     port_y = (port_location / 2) / level->fieldx;
4792
4793     if (port_x < 0 || port_x >= level->fieldx ||
4794         port_y < 0 || port_y >= level->fieldy)
4795     {
4796       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4797
4798       continue;
4799     }
4800
4801     port_element = level->field[port_x][port_y];
4802
4803     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4804         port_element > EL_SP_GRAVITY_PORT_UP)
4805     {
4806       Warn("no special port at position (%d, %d)", port_x, port_y);
4807
4808       continue;
4809     }
4810
4811     // change previous (wrong) gravity inverting special port to either
4812     // gravity enabling special port or gravity disabling special port
4813     level->field[port_x][port_y] +=
4814       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4815        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4816   }
4817
4818   // change special gravity ports without database entries to normal ports
4819   for (x = 0; x < level->fieldx; x++)
4820     for (y = 0; y < level->fieldy; y++)
4821       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4822           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4823         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4824
4825   level->time = 0;                      // no time limit
4826   level->amoeba_speed = 0;
4827   level->time_magic_wall = 0;
4828   level->time_wheel = 0;
4829   level->amoeba_content = EL_EMPTY;
4830
4831   // original Supaplex does not use score values -- rate by playing time
4832   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4833     level->score[i] = 0;
4834
4835   level->rate_time_over_score = TRUE;
4836
4837   // there are no yamyams in supaplex levels
4838   for (i = 0; i < level->num_yamyam_contents; i++)
4839     for (x = 0; x < 3; x++)
4840       for (y = 0; y < 3; y++)
4841         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4842 }
4843
4844 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4845 {
4846   struct LevelInfo_SP *level_sp = level->native_sp_level;
4847   struct DemoInfo_SP *demo = &level_sp->demo;
4848   int i, j;
4849
4850   // always start with reliable default values
4851   demo->is_available = FALSE;
4852   demo->length = 0;
4853
4854   if (TAPE_IS_EMPTY(tape))
4855     return;
4856
4857   demo->level_nr = tape.level_nr;       // (currently not used)
4858
4859   level_sp->header.DemoRandomSeed = tape.random_seed;
4860
4861   demo->length = 0;
4862
4863   for (i = 0; i < tape.length; i++)
4864   {
4865     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4866     int demo_repeat = tape.pos[i].delay;
4867     int demo_entries = (demo_repeat + 15) / 16;
4868
4869     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4870     {
4871       Warn("tape truncated: size exceeds maximum SP demo size %d",
4872            SP_MAX_TAPE_LEN);
4873
4874       break;
4875     }
4876
4877     for (j = 0; j < demo_repeat / 16; j++)
4878       demo->data[demo->length++] = 0xf0 | demo_action;
4879
4880     if (demo_repeat % 16)
4881       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4882   }
4883
4884   demo->is_available = TRUE;
4885 }
4886
4887 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4888 {
4889   struct LevelInfo_SP *level_sp = level->native_sp_level;
4890   struct DemoInfo_SP *demo = &level_sp->demo;
4891   char *filename = level->file_info.filename;
4892   int i;
4893
4894   // always start with reliable default values
4895   setTapeInfoToDefaults();
4896
4897   if (!demo->is_available)
4898     return;
4899
4900   tape.level_nr = demo->level_nr;       // (currently not used)
4901   tape.random_seed = level_sp->header.DemoRandomSeed;
4902
4903   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4904
4905   tape.counter = 0;
4906   tape.pos[tape.counter].delay = 0;
4907
4908   for (i = 0; i < demo->length; i++)
4909   {
4910     int demo_action = demo->data[i] & 0x0f;
4911     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4912     int tape_action = map_key_SP_to_RND(demo_action);
4913     int tape_repeat = demo_repeat + 1;
4914     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4915     boolean success = 0;
4916     int j;
4917
4918     for (j = 0; j < tape_repeat; j++)
4919       success = TapeAddAction(action);
4920
4921     if (!success)
4922     {
4923       Warn("SP demo truncated: size exceeds maximum tape size %d",
4924            MAX_TAPE_LEN);
4925
4926       break;
4927     }
4928   }
4929
4930   TapeHaltRecording();
4931 }
4932
4933
4934 // ----------------------------------------------------------------------------
4935 // functions for loading MM level
4936 // ----------------------------------------------------------------------------
4937
4938 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4939 {
4940   struct LevelInfo_MM *level_mm = level->native_mm_level;
4941   int i, x, y;
4942
4943   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4944   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4945
4946   level_mm->time = level->time;
4947   level_mm->kettles_needed = level->gems_needed;
4948   level_mm->auto_count_kettles = level->auto_count_gems;
4949
4950   level_mm->mm_laser_red   = level->mm_laser_red;
4951   level_mm->mm_laser_green = level->mm_laser_green;
4952   level_mm->mm_laser_blue  = level->mm_laser_blue;
4953
4954   level_mm->df_laser_red   = level->df_laser_red;
4955   level_mm->df_laser_green = level->df_laser_green;
4956   level_mm->df_laser_blue  = level->df_laser_blue;
4957
4958   strcpy(level_mm->name, level->name);
4959   strcpy(level_mm->author, level->author);
4960
4961   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4962   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4963   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4964   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4965   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4966
4967   level_mm->amoeba_speed = level->amoeba_speed;
4968   level_mm->time_fuse    = level->mm_time_fuse;
4969   level_mm->time_bomb    = level->mm_time_bomb;
4970   level_mm->time_ball    = level->mm_time_ball;
4971   level_mm->time_block   = level->mm_time_block;
4972
4973   level_mm->num_ball_contents = level->num_mm_ball_contents;
4974   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4975   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4976   level_mm->explode_ball = level->explode_mm_ball;
4977
4978   for (i = 0; i < level->num_mm_ball_contents; i++)
4979     level_mm->ball_content[i] =
4980       map_element_RND_to_MM(level->mm_ball_content[i]);
4981
4982   for (x = 0; x < level->fieldx; x++)
4983     for (y = 0; y < level->fieldy; y++)
4984       Ur[x][y] =
4985         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4986 }
4987
4988 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4989 {
4990   struct LevelInfo_MM *level_mm = level->native_mm_level;
4991   int i, x, y;
4992
4993   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4994   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4995
4996   level->time = level_mm->time;
4997   level->gems_needed = level_mm->kettles_needed;
4998   level->auto_count_gems = level_mm->auto_count_kettles;
4999
5000   level->mm_laser_red   = level_mm->mm_laser_red;
5001   level->mm_laser_green = level_mm->mm_laser_green;
5002   level->mm_laser_blue  = level_mm->mm_laser_blue;
5003
5004   level->df_laser_red   = level_mm->df_laser_red;
5005   level->df_laser_green = level_mm->df_laser_green;
5006   level->df_laser_blue  = level_mm->df_laser_blue;
5007
5008   strcpy(level->name, level_mm->name);
5009
5010   // only overwrite author from 'levelinfo.conf' if author defined in level
5011   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5012     strcpy(level->author, level_mm->author);
5013
5014   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5015   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5016   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5017   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5018   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5019
5020   level->amoeba_speed  = level_mm->amoeba_speed;
5021   level->mm_time_fuse  = level_mm->time_fuse;
5022   level->mm_time_bomb  = level_mm->time_bomb;
5023   level->mm_time_ball  = level_mm->time_ball;
5024   level->mm_time_block = level_mm->time_block;
5025
5026   level->num_mm_ball_contents = level_mm->num_ball_contents;
5027   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5028   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5029   level->explode_mm_ball = level_mm->explode_ball;
5030
5031   for (i = 0; i < level->num_mm_ball_contents; i++)
5032     level->mm_ball_content[i] =
5033       map_element_MM_to_RND(level_mm->ball_content[i]);
5034
5035   for (x = 0; x < level->fieldx; x++)
5036     for (y = 0; y < level->fieldy; y++)
5037       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5038 }
5039
5040
5041 // ----------------------------------------------------------------------------
5042 // functions for loading DC level
5043 // ----------------------------------------------------------------------------
5044
5045 #define DC_LEVEL_HEADER_SIZE            344
5046
5047 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5048                                         boolean init)
5049 {
5050   static int last_data_encoded;
5051   static int offset1;
5052   static int offset2;
5053   int diff;
5054   int diff_hi, diff_lo;
5055   int data_hi, data_lo;
5056   unsigned short data_decoded;
5057
5058   if (init)
5059   {
5060     last_data_encoded = 0;
5061     offset1 = -1;
5062     offset2 = 0;
5063
5064     return 0;
5065   }
5066
5067   diff = data_encoded - last_data_encoded;
5068   diff_hi = diff & ~0xff;
5069   diff_lo = diff &  0xff;
5070
5071   offset2 += diff_lo;
5072
5073   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5074   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5075   data_hi = data_hi & 0xff00;
5076
5077   data_decoded = data_hi | data_lo;
5078
5079   last_data_encoded = data_encoded;
5080
5081   offset1 = (offset1 + 1) % 31;
5082   offset2 = offset2 & 0xff;
5083
5084   return data_decoded;
5085 }
5086
5087 static int getMappedElement_DC(int element)
5088 {
5089   switch (element)
5090   {
5091     case 0x0000:
5092       element = EL_ROCK;
5093       break;
5094
5095       // 0x0117 - 0x036e: (?)
5096       // EL_DIAMOND
5097
5098       // 0x042d - 0x0684: (?)
5099       // EL_EMERALD
5100
5101     case 0x06f1:
5102       element = EL_NUT;
5103       break;
5104
5105     case 0x074c:
5106       element = EL_BOMB;
5107       break;
5108
5109     case 0x07a4:
5110       element = EL_PEARL;
5111       break;
5112
5113     case 0x0823:
5114       element = EL_CRYSTAL;
5115       break;
5116
5117     case 0x0e77:        // quicksand (boulder)
5118       element = EL_QUICKSAND_FAST_FULL;
5119       break;
5120
5121     case 0x0e99:        // slow quicksand (boulder)
5122       element = EL_QUICKSAND_FULL;
5123       break;
5124
5125     case 0x0ed2:
5126       element = EL_EM_EXIT_OPEN;
5127       break;
5128
5129     case 0x0ee3:
5130       element = EL_EM_EXIT_CLOSED;
5131       break;
5132
5133     case 0x0eeb:
5134       element = EL_EM_STEEL_EXIT_OPEN;
5135       break;
5136
5137     case 0x0efc:
5138       element = EL_EM_STEEL_EXIT_CLOSED;
5139       break;
5140
5141     case 0x0f4f:        // dynamite (lit 1)
5142       element = EL_EM_DYNAMITE_ACTIVE;
5143       break;
5144
5145     case 0x0f57:        // dynamite (lit 2)
5146       element = EL_EM_DYNAMITE_ACTIVE;
5147       break;
5148
5149     case 0x0f5f:        // dynamite (lit 3)
5150       element = EL_EM_DYNAMITE_ACTIVE;
5151       break;
5152
5153     case 0x0f67:        // dynamite (lit 4)
5154       element = EL_EM_DYNAMITE_ACTIVE;
5155       break;
5156
5157     case 0x0f81:
5158     case 0x0f82:
5159     case 0x0f83:
5160     case 0x0f84:
5161       element = EL_AMOEBA_WET;
5162       break;
5163
5164     case 0x0f85:
5165       element = EL_AMOEBA_DROP;
5166       break;
5167
5168     case 0x0fb9:
5169       element = EL_DC_MAGIC_WALL;
5170       break;
5171
5172     case 0x0fd0:
5173       element = EL_SPACESHIP_UP;
5174       break;
5175
5176     case 0x0fd9:
5177       element = EL_SPACESHIP_DOWN;
5178       break;
5179
5180     case 0x0ff1:
5181       element = EL_SPACESHIP_LEFT;
5182       break;
5183
5184     case 0x0ff9:
5185       element = EL_SPACESHIP_RIGHT;
5186       break;
5187
5188     case 0x1057:
5189       element = EL_BUG_UP;
5190       break;
5191
5192     case 0x1060:
5193       element = EL_BUG_DOWN;
5194       break;
5195
5196     case 0x1078:
5197       element = EL_BUG_LEFT;
5198       break;
5199
5200     case 0x1080:
5201       element = EL_BUG_RIGHT;
5202       break;
5203
5204     case 0x10de:
5205       element = EL_MOLE_UP;
5206       break;
5207
5208     case 0x10e7:
5209       element = EL_MOLE_DOWN;
5210       break;
5211
5212     case 0x10ff:
5213       element = EL_MOLE_LEFT;
5214       break;
5215
5216     case 0x1107:
5217       element = EL_MOLE_RIGHT;
5218       break;
5219
5220     case 0x11c0:
5221       element = EL_ROBOT;
5222       break;
5223
5224     case 0x13f5:
5225       element = EL_YAMYAM_UP;
5226       break;
5227
5228     case 0x1425:
5229       element = EL_SWITCHGATE_OPEN;
5230       break;
5231
5232     case 0x1426:
5233       element = EL_SWITCHGATE_CLOSED;
5234       break;
5235
5236     case 0x1437:
5237       element = EL_DC_SWITCHGATE_SWITCH_UP;
5238       break;
5239
5240     case 0x143a:
5241       element = EL_TIMEGATE_CLOSED;
5242       break;
5243
5244     case 0x144c:        // conveyor belt switch (green)
5245       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5246       break;
5247
5248     case 0x144f:        // conveyor belt switch (red)
5249       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5250       break;
5251
5252     case 0x1452:        // conveyor belt switch (blue)
5253       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5254       break;
5255
5256     case 0x145b:
5257       element = EL_CONVEYOR_BELT_3_MIDDLE;
5258       break;
5259
5260     case 0x1463:
5261       element = EL_CONVEYOR_BELT_3_LEFT;
5262       break;
5263
5264     case 0x146b:
5265       element = EL_CONVEYOR_BELT_3_RIGHT;
5266       break;
5267
5268     case 0x1473:
5269       element = EL_CONVEYOR_BELT_1_MIDDLE;
5270       break;
5271
5272     case 0x147b:
5273       element = EL_CONVEYOR_BELT_1_LEFT;
5274       break;
5275
5276     case 0x1483:
5277       element = EL_CONVEYOR_BELT_1_RIGHT;
5278       break;
5279
5280     case 0x148b:
5281       element = EL_CONVEYOR_BELT_4_MIDDLE;
5282       break;
5283
5284     case 0x1493:
5285       element = EL_CONVEYOR_BELT_4_LEFT;
5286       break;
5287
5288     case 0x149b:
5289       element = EL_CONVEYOR_BELT_4_RIGHT;
5290       break;
5291
5292     case 0x14ac:
5293       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5294       break;
5295
5296     case 0x14bd:
5297       element = EL_EXPANDABLE_WALL_VERTICAL;
5298       break;
5299
5300     case 0x14c6:
5301       element = EL_EXPANDABLE_WALL_ANY;
5302       break;
5303
5304     case 0x14ce:        // growing steel wall (left/right)
5305       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5306       break;
5307
5308     case 0x14df:        // growing steel wall (up/down)
5309       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5310       break;
5311
5312     case 0x14e8:        // growing steel wall (up/down/left/right)
5313       element = EL_EXPANDABLE_STEELWALL_ANY;
5314       break;
5315
5316     case 0x14e9:
5317       element = EL_SHIELD_DEADLY;
5318       break;
5319
5320     case 0x1501:
5321       element = EL_EXTRA_TIME;
5322       break;
5323
5324     case 0x154f:
5325       element = EL_ACID;
5326       break;
5327
5328     case 0x1577:
5329       element = EL_EMPTY_SPACE;
5330       break;
5331
5332     case 0x1578:        // quicksand (empty)
5333       element = EL_QUICKSAND_FAST_EMPTY;
5334       break;
5335
5336     case 0x1579:        // slow quicksand (empty)
5337       element = EL_QUICKSAND_EMPTY;
5338       break;
5339
5340       // 0x157c - 0x158b:
5341       // EL_SAND
5342
5343       // 0x1590 - 0x159f:
5344       // EL_DC_LANDMINE
5345
5346     case 0x15a0:
5347       element = EL_EM_DYNAMITE;
5348       break;
5349
5350     case 0x15a1:        // key (red)
5351       element = EL_EM_KEY_1;
5352       break;
5353
5354     case 0x15a2:        // key (yellow)
5355       element = EL_EM_KEY_2;
5356       break;
5357
5358     case 0x15a3:        // key (blue)
5359       element = EL_EM_KEY_4;
5360       break;
5361
5362     case 0x15a4:        // key (green)
5363       element = EL_EM_KEY_3;
5364       break;
5365
5366     case 0x15a5:        // key (white)
5367       element = EL_DC_KEY_WHITE;
5368       break;
5369
5370     case 0x15a6:
5371       element = EL_WALL_SLIPPERY;
5372       break;
5373
5374     case 0x15a7:
5375       element = EL_WALL;
5376       break;
5377
5378     case 0x15a8:        // wall (not round)
5379       element = EL_WALL;
5380       break;
5381
5382     case 0x15a9:        // (blue)
5383       element = EL_CHAR_A;
5384       break;
5385
5386     case 0x15aa:        // (blue)
5387       element = EL_CHAR_B;
5388       break;
5389
5390     case 0x15ab:        // (blue)
5391       element = EL_CHAR_C;
5392       break;
5393
5394     case 0x15ac:        // (blue)
5395       element = EL_CHAR_D;
5396       break;
5397
5398     case 0x15ad:        // (blue)
5399       element = EL_CHAR_E;
5400       break;
5401
5402     case 0x15ae:        // (blue)
5403       element = EL_CHAR_F;
5404       break;
5405
5406     case 0x15af:        // (blue)
5407       element = EL_CHAR_G;
5408       break;
5409
5410     case 0x15b0:        // (blue)
5411       element = EL_CHAR_H;
5412       break;
5413
5414     case 0x15b1:        // (blue)
5415       element = EL_CHAR_I;
5416       break;
5417
5418     case 0x15b2:        // (blue)
5419       element = EL_CHAR_J;
5420       break;
5421
5422     case 0x15b3:        // (blue)
5423       element = EL_CHAR_K;
5424       break;
5425
5426     case 0x15b4:        // (blue)
5427       element = EL_CHAR_L;
5428       break;
5429
5430     case 0x15b5:        // (blue)
5431       element = EL_CHAR_M;
5432       break;
5433
5434     case 0x15b6:        // (blue)
5435       element = EL_CHAR_N;
5436       break;
5437
5438     case 0x15b7:        // (blue)
5439       element = EL_CHAR_O;
5440       break;
5441
5442     case 0x15b8:        // (blue)
5443       element = EL_CHAR_P;
5444       break;
5445
5446     case 0x15b9:        // (blue)
5447       element = EL_CHAR_Q;
5448       break;
5449
5450     case 0x15ba:        // (blue)
5451       element = EL_CHAR_R;
5452       break;
5453
5454     case 0x15bb:        // (blue)
5455       element = EL_CHAR_S;
5456       break;
5457
5458     case 0x15bc:        // (blue)
5459       element = EL_CHAR_T;
5460       break;
5461
5462     case 0x15bd:        // (blue)
5463       element = EL_CHAR_U;
5464       break;
5465
5466     case 0x15be:        // (blue)
5467       element = EL_CHAR_V;
5468       break;
5469
5470     case 0x15bf:        // (blue)
5471       element = EL_CHAR_W;
5472       break;
5473
5474     case 0x15c0:        // (blue)
5475       element = EL_CHAR_X;
5476       break;
5477
5478     case 0x15c1:        // (blue)
5479       element = EL_CHAR_Y;
5480       break;
5481
5482     case 0x15c2:        // (blue)
5483       element = EL_CHAR_Z;
5484       break;
5485
5486     case 0x15c3:        // (blue)
5487       element = EL_CHAR_AUMLAUT;
5488       break;
5489
5490     case 0x15c4:        // (blue)
5491       element = EL_CHAR_OUMLAUT;
5492       break;
5493
5494     case 0x15c5:        // (blue)
5495       element = EL_CHAR_UUMLAUT;
5496       break;
5497
5498     case 0x15c6:        // (blue)
5499       element = EL_CHAR_0;
5500       break;
5501
5502     case 0x15c7:        // (blue)
5503       element = EL_CHAR_1;
5504       break;
5505
5506     case 0x15c8:        // (blue)
5507       element = EL_CHAR_2;
5508       break;
5509
5510     case 0x15c9:        // (blue)
5511       element = EL_CHAR_3;
5512       break;
5513
5514     case 0x15ca:        // (blue)
5515       element = EL_CHAR_4;
5516       break;
5517
5518     case 0x15cb:        // (blue)
5519       element = EL_CHAR_5;
5520       break;
5521
5522     case 0x15cc:        // (blue)
5523       element = EL_CHAR_6;
5524       break;
5525
5526     case 0x15cd:        // (blue)
5527       element = EL_CHAR_7;
5528       break;
5529
5530     case 0x15ce:        // (blue)
5531       element = EL_CHAR_8;
5532       break;
5533
5534     case 0x15cf:        // (blue)
5535       element = EL_CHAR_9;
5536       break;
5537
5538     case 0x15d0:        // (blue)
5539       element = EL_CHAR_PERIOD;
5540       break;
5541
5542     case 0x15d1:        // (blue)
5543       element = EL_CHAR_EXCLAM;
5544       break;
5545
5546     case 0x15d2:        // (blue)
5547       element = EL_CHAR_COLON;
5548       break;
5549
5550     case 0x15d3:        // (blue)
5551       element = EL_CHAR_LESS;
5552       break;
5553
5554     case 0x15d4:        // (blue)
5555       element = EL_CHAR_GREATER;
5556       break;
5557
5558     case 0x15d5:        // (blue)
5559       element = EL_CHAR_QUESTION;
5560       break;
5561
5562     case 0x15d6:        // (blue)
5563       element = EL_CHAR_COPYRIGHT;
5564       break;
5565
5566     case 0x15d7:        // (blue)
5567       element = EL_CHAR_UP;
5568       break;
5569
5570     case 0x15d8:        // (blue)
5571       element = EL_CHAR_DOWN;
5572       break;
5573
5574     case 0x15d9:        // (blue)
5575       element = EL_CHAR_BUTTON;
5576       break;
5577
5578     case 0x15da:        // (blue)
5579       element = EL_CHAR_PLUS;
5580       break;
5581
5582     case 0x15db:        // (blue)
5583       element = EL_CHAR_MINUS;
5584       break;
5585
5586     case 0x15dc:        // (blue)
5587       element = EL_CHAR_APOSTROPHE;
5588       break;
5589
5590     case 0x15dd:        // (blue)
5591       element = EL_CHAR_PARENLEFT;
5592       break;
5593
5594     case 0x15de:        // (blue)
5595       element = EL_CHAR_PARENRIGHT;
5596       break;
5597
5598     case 0x15df:        // (green)
5599       element = EL_CHAR_A;
5600       break;
5601
5602     case 0x15e0:        // (green)
5603       element = EL_CHAR_B;
5604       break;
5605
5606     case 0x15e1:        // (green)
5607       element = EL_CHAR_C;
5608       break;
5609
5610     case 0x15e2:        // (green)
5611       element = EL_CHAR_D;
5612       break;
5613
5614     case 0x15e3:        // (green)
5615       element = EL_CHAR_E;
5616       break;
5617
5618     case 0x15e4:        // (green)
5619       element = EL_CHAR_F;
5620       break;
5621
5622     case 0x15e5:        // (green)
5623       element = EL_CHAR_G;
5624       break;
5625
5626     case 0x15e6:        // (green)
5627       element = EL_CHAR_H;
5628       break;
5629
5630     case 0x15e7:        // (green)
5631       element = EL_CHAR_I;
5632       break;
5633
5634     case 0x15e8:        // (green)
5635       element = EL_CHAR_J;
5636       break;
5637
5638     case 0x15e9:        // (green)
5639       element = EL_CHAR_K;
5640       break;
5641
5642     case 0x15ea:        // (green)
5643       element = EL_CHAR_L;
5644       break;
5645
5646     case 0x15eb:        // (green)
5647       element = EL_CHAR_M;
5648       break;
5649
5650     case 0x15ec:        // (green)
5651       element = EL_CHAR_N;
5652       break;
5653
5654     case 0x15ed:        // (green)
5655       element = EL_CHAR_O;
5656       break;
5657
5658     case 0x15ee:        // (green)
5659       element = EL_CHAR_P;
5660       break;
5661
5662     case 0x15ef:        // (green)
5663       element = EL_CHAR_Q;
5664       break;
5665
5666     case 0x15f0:        // (green)
5667       element = EL_CHAR_R;
5668       break;
5669
5670     case 0x15f1:        // (green)
5671       element = EL_CHAR_S;
5672       break;
5673
5674     case 0x15f2:        // (green)
5675       element = EL_CHAR_T;
5676       break;
5677
5678     case 0x15f3:        // (green)
5679       element = EL_CHAR_U;
5680       break;
5681
5682     case 0x15f4:        // (green)
5683       element = EL_CHAR_V;
5684       break;
5685
5686     case 0x15f5:        // (green)
5687       element = EL_CHAR_W;
5688       break;
5689
5690     case 0x15f6:        // (green)
5691       element = EL_CHAR_X;
5692       break;
5693
5694     case 0x15f7:        // (green)
5695       element = EL_CHAR_Y;
5696       break;
5697
5698     case 0x15f8:        // (green)
5699       element = EL_CHAR_Z;
5700       break;
5701
5702     case 0x15f9:        // (green)
5703       element = EL_CHAR_AUMLAUT;
5704       break;
5705
5706     case 0x15fa:        // (green)
5707       element = EL_CHAR_OUMLAUT;
5708       break;
5709
5710     case 0x15fb:        // (green)
5711       element = EL_CHAR_UUMLAUT;
5712       break;
5713
5714     case 0x15fc:        // (green)
5715       element = EL_CHAR_0;
5716       break;
5717
5718     case 0x15fd:        // (green)
5719       element = EL_CHAR_1;
5720       break;
5721
5722     case 0x15fe:        // (green)
5723       element = EL_CHAR_2;
5724       break;
5725
5726     case 0x15ff:        // (green)
5727       element = EL_CHAR_3;
5728       break;
5729
5730     case 0x1600:        // (green)
5731       element = EL_CHAR_4;
5732       break;
5733
5734     case 0x1601:        // (green)
5735       element = EL_CHAR_5;
5736       break;
5737
5738     case 0x1602:        // (green)
5739       element = EL_CHAR_6;
5740       break;
5741
5742     case 0x1603:        // (green)
5743       element = EL_CHAR_7;
5744       break;
5745
5746     case 0x1604:        // (green)
5747       element = EL_CHAR_8;
5748       break;
5749
5750     case 0x1605:        // (green)
5751       element = EL_CHAR_9;
5752       break;
5753
5754     case 0x1606:        // (green)
5755       element = EL_CHAR_PERIOD;
5756       break;
5757
5758     case 0x1607:        // (green)
5759       element = EL_CHAR_EXCLAM;
5760       break;
5761
5762     case 0x1608:        // (green)
5763       element = EL_CHAR_COLON;
5764       break;
5765
5766     case 0x1609:        // (green)
5767       element = EL_CHAR_LESS;
5768       break;
5769
5770     case 0x160a:        // (green)
5771       element = EL_CHAR_GREATER;
5772       break;
5773
5774     case 0x160b:        // (green)
5775       element = EL_CHAR_QUESTION;
5776       break;
5777
5778     case 0x160c:        // (green)
5779       element = EL_CHAR_COPYRIGHT;
5780       break;
5781
5782     case 0x160d:        // (green)
5783       element = EL_CHAR_UP;
5784       break;
5785
5786     case 0x160e:        // (green)
5787       element = EL_CHAR_DOWN;
5788       break;
5789
5790     case 0x160f:        // (green)
5791       element = EL_CHAR_BUTTON;
5792       break;
5793
5794     case 0x1610:        // (green)
5795       element = EL_CHAR_PLUS;
5796       break;
5797
5798     case 0x1611:        // (green)
5799       element = EL_CHAR_MINUS;
5800       break;
5801
5802     case 0x1612:        // (green)
5803       element = EL_CHAR_APOSTROPHE;
5804       break;
5805
5806     case 0x1613:        // (green)
5807       element = EL_CHAR_PARENLEFT;
5808       break;
5809
5810     case 0x1614:        // (green)
5811       element = EL_CHAR_PARENRIGHT;
5812       break;
5813
5814     case 0x1615:        // (blue steel)
5815       element = EL_STEEL_CHAR_A;
5816       break;
5817
5818     case 0x1616:        // (blue steel)
5819       element = EL_STEEL_CHAR_B;
5820       break;
5821
5822     case 0x1617:        // (blue steel)
5823       element = EL_STEEL_CHAR_C;
5824       break;
5825
5826     case 0x1618:        // (blue steel)
5827       element = EL_STEEL_CHAR_D;
5828       break;
5829
5830     case 0x1619:        // (blue steel)
5831       element = EL_STEEL_CHAR_E;
5832       break;
5833
5834     case 0x161a:        // (blue steel)
5835       element = EL_STEEL_CHAR_F;
5836       break;
5837
5838     case 0x161b:        // (blue steel)
5839       element = EL_STEEL_CHAR_G;
5840       break;
5841
5842     case 0x161c:        // (blue steel)
5843       element = EL_STEEL_CHAR_H;
5844       break;
5845
5846     case 0x161d:        // (blue steel)
5847       element = EL_STEEL_CHAR_I;
5848       break;
5849
5850     case 0x161e:        // (blue steel)
5851       element = EL_STEEL_CHAR_J;
5852       break;
5853
5854     case 0x161f:        // (blue steel)
5855       element = EL_STEEL_CHAR_K;
5856       break;
5857
5858     case 0x1620:        // (blue steel)
5859       element = EL_STEEL_CHAR_L;
5860       break;
5861
5862     case 0x1621:        // (blue steel)
5863       element = EL_STEEL_CHAR_M;
5864       break;
5865
5866     case 0x1622:        // (blue steel)
5867       element = EL_STEEL_CHAR_N;
5868       break;
5869
5870     case 0x1623:        // (blue steel)
5871       element = EL_STEEL_CHAR_O;
5872       break;
5873
5874     case 0x1624:        // (blue steel)
5875       element = EL_STEEL_CHAR_P;
5876       break;
5877
5878     case 0x1625:        // (blue steel)
5879       element = EL_STEEL_CHAR_Q;
5880       break;
5881
5882     case 0x1626:        // (blue steel)
5883       element = EL_STEEL_CHAR_R;
5884       break;
5885
5886     case 0x1627:        // (blue steel)
5887       element = EL_STEEL_CHAR_S;
5888       break;
5889
5890     case 0x1628:        // (blue steel)
5891       element = EL_STEEL_CHAR_T;
5892       break;
5893
5894     case 0x1629:        // (blue steel)
5895       element = EL_STEEL_CHAR_U;
5896       break;
5897
5898     case 0x162a:        // (blue steel)
5899       element = EL_STEEL_CHAR_V;
5900       break;
5901
5902     case 0x162b:        // (blue steel)
5903       element = EL_STEEL_CHAR_W;
5904       break;
5905
5906     case 0x162c:        // (blue steel)
5907       element = EL_STEEL_CHAR_X;
5908       break;
5909
5910     case 0x162d:        // (blue steel)
5911       element = EL_STEEL_CHAR_Y;
5912       break;
5913
5914     case 0x162e:        // (blue steel)
5915       element = EL_STEEL_CHAR_Z;
5916       break;
5917
5918     case 0x162f:        // (blue steel)
5919       element = EL_STEEL_CHAR_AUMLAUT;
5920       break;
5921
5922     case 0x1630:        // (blue steel)
5923       element = EL_STEEL_CHAR_OUMLAUT;
5924       break;
5925
5926     case 0x1631:        // (blue steel)
5927       element = EL_STEEL_CHAR_UUMLAUT;
5928       break;
5929
5930     case 0x1632:        // (blue steel)
5931       element = EL_STEEL_CHAR_0;
5932       break;
5933
5934     case 0x1633:        // (blue steel)
5935       element = EL_STEEL_CHAR_1;
5936       break;
5937
5938     case 0x1634:        // (blue steel)
5939       element = EL_STEEL_CHAR_2;
5940       break;
5941
5942     case 0x1635:        // (blue steel)
5943       element = EL_STEEL_CHAR_3;
5944       break;
5945
5946     case 0x1636:        // (blue steel)
5947       element = EL_STEEL_CHAR_4;
5948       break;
5949
5950     case 0x1637:        // (blue steel)
5951       element = EL_STEEL_CHAR_5;
5952       break;
5953
5954     case 0x1638:        // (blue steel)
5955       element = EL_STEEL_CHAR_6;
5956       break;
5957
5958     case 0x1639:        // (blue steel)
5959       element = EL_STEEL_CHAR_7;
5960       break;
5961
5962     case 0x163a:        // (blue steel)
5963       element = EL_STEEL_CHAR_8;
5964       break;
5965
5966     case 0x163b:        // (blue steel)
5967       element = EL_STEEL_CHAR_9;
5968       break;
5969
5970     case 0x163c:        // (blue steel)
5971       element = EL_STEEL_CHAR_PERIOD;
5972       break;
5973
5974     case 0x163d:        // (blue steel)
5975       element = EL_STEEL_CHAR_EXCLAM;
5976       break;
5977
5978     case 0x163e:        // (blue steel)
5979       element = EL_STEEL_CHAR_COLON;
5980       break;
5981
5982     case 0x163f:        // (blue steel)
5983       element = EL_STEEL_CHAR_LESS;
5984       break;
5985
5986     case 0x1640:        // (blue steel)
5987       element = EL_STEEL_CHAR_GREATER;
5988       break;
5989
5990     case 0x1641:        // (blue steel)
5991       element = EL_STEEL_CHAR_QUESTION;
5992       break;
5993
5994     case 0x1642:        // (blue steel)
5995       element = EL_STEEL_CHAR_COPYRIGHT;
5996       break;
5997
5998     case 0x1643:        // (blue steel)
5999       element = EL_STEEL_CHAR_UP;
6000       break;
6001
6002     case 0x1644:        // (blue steel)
6003       element = EL_STEEL_CHAR_DOWN;
6004       break;
6005
6006     case 0x1645:        // (blue steel)
6007       element = EL_STEEL_CHAR_BUTTON;
6008       break;
6009
6010     case 0x1646:        // (blue steel)
6011       element = EL_STEEL_CHAR_PLUS;
6012       break;
6013
6014     case 0x1647:        // (blue steel)
6015       element = EL_STEEL_CHAR_MINUS;
6016       break;
6017
6018     case 0x1648:        // (blue steel)
6019       element = EL_STEEL_CHAR_APOSTROPHE;
6020       break;
6021
6022     case 0x1649:        // (blue steel)
6023       element = EL_STEEL_CHAR_PARENLEFT;
6024       break;
6025
6026     case 0x164a:        // (blue steel)
6027       element = EL_STEEL_CHAR_PARENRIGHT;
6028       break;
6029
6030     case 0x164b:        // (green steel)
6031       element = EL_STEEL_CHAR_A;
6032       break;
6033
6034     case 0x164c:        // (green steel)
6035       element = EL_STEEL_CHAR_B;
6036       break;
6037
6038     case 0x164d:        // (green steel)
6039       element = EL_STEEL_CHAR_C;
6040       break;
6041
6042     case 0x164e:        // (green steel)
6043       element = EL_STEEL_CHAR_D;
6044       break;
6045
6046     case 0x164f:        // (green steel)
6047       element = EL_STEEL_CHAR_E;
6048       break;
6049
6050     case 0x1650:        // (green steel)
6051       element = EL_STEEL_CHAR_F;
6052       break;
6053
6054     case 0x1651:        // (green steel)
6055       element = EL_STEEL_CHAR_G;
6056       break;
6057
6058     case 0x1652:        // (green steel)
6059       element = EL_STEEL_CHAR_H;
6060       break;
6061
6062     case 0x1653:        // (green steel)
6063       element = EL_STEEL_CHAR_I;
6064       break;
6065
6066     case 0x1654:        // (green steel)
6067       element = EL_STEEL_CHAR_J;
6068       break;
6069
6070     case 0x1655:        // (green steel)
6071       element = EL_STEEL_CHAR_K;
6072       break;
6073
6074     case 0x1656:        // (green steel)
6075       element = EL_STEEL_CHAR_L;
6076       break;
6077
6078     case 0x1657:        // (green steel)
6079       element = EL_STEEL_CHAR_M;
6080       break;
6081
6082     case 0x1658:        // (green steel)
6083       element = EL_STEEL_CHAR_N;
6084       break;
6085
6086     case 0x1659:        // (green steel)
6087       element = EL_STEEL_CHAR_O;
6088       break;
6089
6090     case 0x165a:        // (green steel)
6091       element = EL_STEEL_CHAR_P;
6092       break;
6093
6094     case 0x165b:        // (green steel)
6095       element = EL_STEEL_CHAR_Q;
6096       break;
6097
6098     case 0x165c:        // (green steel)
6099       element = EL_STEEL_CHAR_R;
6100       break;
6101
6102     case 0x165d:        // (green steel)
6103       element = EL_STEEL_CHAR_S;
6104       break;
6105
6106     case 0x165e:        // (green steel)
6107       element = EL_STEEL_CHAR_T;
6108       break;
6109
6110     case 0x165f:        // (green steel)
6111       element = EL_STEEL_CHAR_U;
6112       break;
6113
6114     case 0x1660:        // (green steel)
6115       element = EL_STEEL_CHAR_V;
6116       break;
6117
6118     case 0x1661:        // (green steel)
6119       element = EL_STEEL_CHAR_W;
6120       break;
6121
6122     case 0x1662:        // (green steel)
6123       element = EL_STEEL_CHAR_X;
6124       break;
6125
6126     case 0x1663:        // (green steel)
6127       element = EL_STEEL_CHAR_Y;
6128       break;
6129
6130     case 0x1664:        // (green steel)
6131       element = EL_STEEL_CHAR_Z;
6132       break;
6133
6134     case 0x1665:        // (green steel)
6135       element = EL_STEEL_CHAR_AUMLAUT;
6136       break;
6137
6138     case 0x1666:        // (green steel)
6139       element = EL_STEEL_CHAR_OUMLAUT;
6140       break;
6141
6142     case 0x1667:        // (green steel)
6143       element = EL_STEEL_CHAR_UUMLAUT;
6144       break;
6145
6146     case 0x1668:        // (green steel)
6147       element = EL_STEEL_CHAR_0;
6148       break;
6149
6150     case 0x1669:        // (green steel)
6151       element = EL_STEEL_CHAR_1;
6152       break;
6153
6154     case 0x166a:        // (green steel)
6155       element = EL_STEEL_CHAR_2;
6156       break;
6157
6158     case 0x166b:        // (green steel)
6159       element = EL_STEEL_CHAR_3;
6160       break;
6161
6162     case 0x166c:        // (green steel)
6163       element = EL_STEEL_CHAR_4;
6164       break;
6165
6166     case 0x166d:        // (green steel)
6167       element = EL_STEEL_CHAR_5;
6168       break;
6169
6170     case 0x166e:        // (green steel)
6171       element = EL_STEEL_CHAR_6;
6172       break;
6173
6174     case 0x166f:        // (green steel)
6175       element = EL_STEEL_CHAR_7;
6176       break;
6177
6178     case 0x1670:        // (green steel)
6179       element = EL_STEEL_CHAR_8;
6180       break;
6181
6182     case 0x1671:        // (green steel)
6183       element = EL_STEEL_CHAR_9;
6184       break;
6185
6186     case 0x1672:        // (green steel)
6187       element = EL_STEEL_CHAR_PERIOD;
6188       break;
6189
6190     case 0x1673:        // (green steel)
6191       element = EL_STEEL_CHAR_EXCLAM;
6192       break;
6193
6194     case 0x1674:        // (green steel)
6195       element = EL_STEEL_CHAR_COLON;
6196       break;
6197
6198     case 0x1675:        // (green steel)
6199       element = EL_STEEL_CHAR_LESS;
6200       break;
6201
6202     case 0x1676:        // (green steel)
6203       element = EL_STEEL_CHAR_GREATER;
6204       break;
6205
6206     case 0x1677:        // (green steel)
6207       element = EL_STEEL_CHAR_QUESTION;
6208       break;
6209
6210     case 0x1678:        // (green steel)
6211       element = EL_STEEL_CHAR_COPYRIGHT;
6212       break;
6213
6214     case 0x1679:        // (green steel)
6215       element = EL_STEEL_CHAR_UP;
6216       break;
6217
6218     case 0x167a:        // (green steel)
6219       element = EL_STEEL_CHAR_DOWN;
6220       break;
6221
6222     case 0x167b:        // (green steel)
6223       element = EL_STEEL_CHAR_BUTTON;
6224       break;
6225
6226     case 0x167c:        // (green steel)
6227       element = EL_STEEL_CHAR_PLUS;
6228       break;
6229
6230     case 0x167d:        // (green steel)
6231       element = EL_STEEL_CHAR_MINUS;
6232       break;
6233
6234     case 0x167e:        // (green steel)
6235       element = EL_STEEL_CHAR_APOSTROPHE;
6236       break;
6237
6238     case 0x167f:        // (green steel)
6239       element = EL_STEEL_CHAR_PARENLEFT;
6240       break;
6241
6242     case 0x1680:        // (green steel)
6243       element = EL_STEEL_CHAR_PARENRIGHT;
6244       break;
6245
6246     case 0x1681:        // gate (red)
6247       element = EL_EM_GATE_1;
6248       break;
6249
6250     case 0x1682:        // secret gate (red)
6251       element = EL_EM_GATE_1_GRAY;
6252       break;
6253
6254     case 0x1683:        // gate (yellow)
6255       element = EL_EM_GATE_2;
6256       break;
6257
6258     case 0x1684:        // secret gate (yellow)
6259       element = EL_EM_GATE_2_GRAY;
6260       break;
6261
6262     case 0x1685:        // gate (blue)
6263       element = EL_EM_GATE_4;
6264       break;
6265
6266     case 0x1686:        // secret gate (blue)
6267       element = EL_EM_GATE_4_GRAY;
6268       break;
6269
6270     case 0x1687:        // gate (green)
6271       element = EL_EM_GATE_3;
6272       break;
6273
6274     case 0x1688:        // secret gate (green)
6275       element = EL_EM_GATE_3_GRAY;
6276       break;
6277
6278     case 0x1689:        // gate (white)
6279       element = EL_DC_GATE_WHITE;
6280       break;
6281
6282     case 0x168a:        // secret gate (white)
6283       element = EL_DC_GATE_WHITE_GRAY;
6284       break;
6285
6286     case 0x168b:        // secret gate (no key)
6287       element = EL_DC_GATE_FAKE_GRAY;
6288       break;
6289
6290     case 0x168c:
6291       element = EL_ROBOT_WHEEL;
6292       break;
6293
6294     case 0x168d:
6295       element = EL_DC_TIMEGATE_SWITCH;
6296       break;
6297
6298     case 0x168e:
6299       element = EL_ACID_POOL_BOTTOM;
6300       break;
6301
6302     case 0x168f:
6303       element = EL_ACID_POOL_TOPLEFT;
6304       break;
6305
6306     case 0x1690:
6307       element = EL_ACID_POOL_TOPRIGHT;
6308       break;
6309
6310     case 0x1691:
6311       element = EL_ACID_POOL_BOTTOMLEFT;
6312       break;
6313
6314     case 0x1692:
6315       element = EL_ACID_POOL_BOTTOMRIGHT;
6316       break;
6317
6318     case 0x1693:
6319       element = EL_STEELWALL;
6320       break;
6321
6322     case 0x1694:
6323       element = EL_STEELWALL_SLIPPERY;
6324       break;
6325
6326     case 0x1695:        // steel wall (not round)
6327       element = EL_STEELWALL;
6328       break;
6329
6330     case 0x1696:        // steel wall (left)
6331       element = EL_DC_STEELWALL_1_LEFT;
6332       break;
6333
6334     case 0x1697:        // steel wall (bottom)
6335       element = EL_DC_STEELWALL_1_BOTTOM;
6336       break;
6337
6338     case 0x1698:        // steel wall (right)
6339       element = EL_DC_STEELWALL_1_RIGHT;
6340       break;
6341
6342     case 0x1699:        // steel wall (top)
6343       element = EL_DC_STEELWALL_1_TOP;
6344       break;
6345
6346     case 0x169a:        // steel wall (left/bottom)
6347       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6348       break;
6349
6350     case 0x169b:        // steel wall (right/bottom)
6351       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6352       break;
6353
6354     case 0x169c:        // steel wall (right/top)
6355       element = EL_DC_STEELWALL_1_TOPRIGHT;
6356       break;
6357
6358     case 0x169d:        // steel wall (left/top)
6359       element = EL_DC_STEELWALL_1_TOPLEFT;
6360       break;
6361
6362     case 0x169e:        // steel wall (right/bottom small)
6363       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6364       break;
6365
6366     case 0x169f:        // steel wall (left/bottom small)
6367       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6368       break;
6369
6370     case 0x16a0:        // steel wall (right/top small)
6371       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6372       break;
6373
6374     case 0x16a1:        // steel wall (left/top small)
6375       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6376       break;
6377
6378     case 0x16a2:        // steel wall (left/right)
6379       element = EL_DC_STEELWALL_1_VERTICAL;
6380       break;
6381
6382     case 0x16a3:        // steel wall (top/bottom)
6383       element = EL_DC_STEELWALL_1_HORIZONTAL;
6384       break;
6385
6386     case 0x16a4:        // steel wall 2 (left end)
6387       element = EL_DC_STEELWALL_2_LEFT;
6388       break;
6389
6390     case 0x16a5:        // steel wall 2 (right end)
6391       element = EL_DC_STEELWALL_2_RIGHT;
6392       break;
6393
6394     case 0x16a6:        // steel wall 2 (top end)
6395       element = EL_DC_STEELWALL_2_TOP;
6396       break;
6397
6398     case 0x16a7:        // steel wall 2 (bottom end)
6399       element = EL_DC_STEELWALL_2_BOTTOM;
6400       break;
6401
6402     case 0x16a8:        // steel wall 2 (left/right)
6403       element = EL_DC_STEELWALL_2_HORIZONTAL;
6404       break;
6405
6406     case 0x16a9:        // steel wall 2 (up/down)
6407       element = EL_DC_STEELWALL_2_VERTICAL;
6408       break;
6409
6410     case 0x16aa:        // steel wall 2 (mid)
6411       element = EL_DC_STEELWALL_2_MIDDLE;
6412       break;
6413
6414     case 0x16ab:
6415       element = EL_SIGN_EXCLAMATION;
6416       break;
6417
6418     case 0x16ac:
6419       element = EL_SIGN_RADIOACTIVITY;
6420       break;
6421
6422     case 0x16ad:
6423       element = EL_SIGN_STOP;
6424       break;
6425
6426     case 0x16ae:
6427       element = EL_SIGN_WHEELCHAIR;
6428       break;
6429
6430     case 0x16af:
6431       element = EL_SIGN_PARKING;
6432       break;
6433
6434     case 0x16b0:
6435       element = EL_SIGN_NO_ENTRY;
6436       break;
6437
6438     case 0x16b1:
6439       element = EL_SIGN_HEART;
6440       break;
6441
6442     case 0x16b2:
6443       element = EL_SIGN_GIVE_WAY;
6444       break;
6445
6446     case 0x16b3:
6447       element = EL_SIGN_ENTRY_FORBIDDEN;
6448       break;
6449
6450     case 0x16b4:
6451       element = EL_SIGN_EMERGENCY_EXIT;
6452       break;
6453
6454     case 0x16b5:
6455       element = EL_SIGN_YIN_YANG;
6456       break;
6457
6458     case 0x16b6:
6459       element = EL_WALL_EMERALD;
6460       break;
6461
6462     case 0x16b7:
6463       element = EL_WALL_DIAMOND;
6464       break;
6465
6466     case 0x16b8:
6467       element = EL_WALL_PEARL;
6468       break;
6469
6470     case 0x16b9:
6471       element = EL_WALL_CRYSTAL;
6472       break;
6473
6474     case 0x16ba:
6475       element = EL_INVISIBLE_WALL;
6476       break;
6477
6478     case 0x16bb:
6479       element = EL_INVISIBLE_STEELWALL;
6480       break;
6481
6482       // 0x16bc - 0x16cb:
6483       // EL_INVISIBLE_SAND
6484
6485     case 0x16cc:
6486       element = EL_LIGHT_SWITCH;
6487       break;
6488
6489     case 0x16cd:
6490       element = EL_ENVELOPE_1;
6491       break;
6492
6493     default:
6494       if (element >= 0x0117 && element <= 0x036e)       // (?)
6495         element = EL_DIAMOND;
6496       else if (element >= 0x042d && element <= 0x0684)  // (?)
6497         element = EL_EMERALD;
6498       else if (element >= 0x157c && element <= 0x158b)
6499         element = EL_SAND;
6500       else if (element >= 0x1590 && element <= 0x159f)
6501         element = EL_DC_LANDMINE;
6502       else if (element >= 0x16bc && element <= 0x16cb)
6503         element = EL_INVISIBLE_SAND;
6504       else
6505       {
6506         Warn("unknown Diamond Caves element 0x%04x", element);
6507
6508         element = EL_UNKNOWN;
6509       }
6510       break;
6511   }
6512
6513   return getMappedElement(element);
6514 }
6515
6516 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6517 {
6518   byte header[DC_LEVEL_HEADER_SIZE];
6519   int envelope_size;
6520   int envelope_header_pos = 62;
6521   int envelope_content_pos = 94;
6522   int level_name_pos = 251;
6523   int level_author_pos = 292;
6524   int envelope_header_len;
6525   int envelope_content_len;
6526   int level_name_len;
6527   int level_author_len;
6528   int fieldx, fieldy;
6529   int num_yamyam_contents;
6530   int i, x, y;
6531
6532   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6533
6534   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6535   {
6536     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6537
6538     header[i * 2 + 0] = header_word >> 8;
6539     header[i * 2 + 1] = header_word & 0xff;
6540   }
6541
6542   // read some values from level header to check level decoding integrity
6543   fieldx = header[6] | (header[7] << 8);
6544   fieldy = header[8] | (header[9] << 8);
6545   num_yamyam_contents = header[60] | (header[61] << 8);
6546
6547   // do some simple sanity checks to ensure that level was correctly decoded
6548   if (fieldx < 1 || fieldx > 256 ||
6549       fieldy < 1 || fieldy > 256 ||
6550       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6551   {
6552     level->no_valid_file = TRUE;
6553
6554     Warn("cannot decode level from stream -- using empty level");
6555
6556     return;
6557   }
6558
6559   // maximum envelope header size is 31 bytes
6560   envelope_header_len   = header[envelope_header_pos];
6561   // maximum envelope content size is 110 (156?) bytes
6562   envelope_content_len  = header[envelope_content_pos];
6563
6564   // maximum level title size is 40 bytes
6565   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6566   // maximum level author size is 30 (51?) bytes
6567   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6568
6569   envelope_size = 0;
6570
6571   for (i = 0; i < envelope_header_len; i++)
6572     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6573       level->envelope[0].text[envelope_size++] =
6574         header[envelope_header_pos + 1 + i];
6575
6576   if (envelope_header_len > 0 && envelope_content_len > 0)
6577   {
6578     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6579       level->envelope[0].text[envelope_size++] = '\n';
6580     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6581       level->envelope[0].text[envelope_size++] = '\n';
6582   }
6583
6584   for (i = 0; i < envelope_content_len; i++)
6585     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6586       level->envelope[0].text[envelope_size++] =
6587         header[envelope_content_pos + 1 + i];
6588
6589   level->envelope[0].text[envelope_size] = '\0';
6590
6591   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6592   level->envelope[0].ysize = 10;
6593   level->envelope[0].autowrap = TRUE;
6594   level->envelope[0].centered = TRUE;
6595
6596   for (i = 0; i < level_name_len; i++)
6597     level->name[i] = header[level_name_pos + 1 + i];
6598   level->name[level_name_len] = '\0';
6599
6600   for (i = 0; i < level_author_len; i++)
6601     level->author[i] = header[level_author_pos + 1 + i];
6602   level->author[level_author_len] = '\0';
6603
6604   num_yamyam_contents = header[60] | (header[61] << 8);
6605   level->num_yamyam_contents =
6606     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6607
6608   for (i = 0; i < num_yamyam_contents; i++)
6609   {
6610     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6611     {
6612       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6613       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6614
6615       if (i < MAX_ELEMENT_CONTENTS)
6616         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6617     }
6618   }
6619
6620   fieldx = header[6] | (header[7] << 8);
6621   fieldy = header[8] | (header[9] << 8);
6622   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6623   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6624
6625   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6626   {
6627     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6628     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6629
6630     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6631       level->field[x][y] = getMappedElement_DC(element_dc);
6632   }
6633
6634   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6635   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6636   level->field[x][y] = EL_PLAYER_1;
6637
6638   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6639   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6640   level->field[x][y] = EL_PLAYER_2;
6641
6642   level->gems_needed            = header[18] | (header[19] << 8);
6643
6644   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6645   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6646   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6647   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6648   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6649   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6650   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6651   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6652   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6653   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6654   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6655   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6656
6657   level->time                   = header[44] | (header[45] << 8);
6658
6659   level->amoeba_speed           = header[46] | (header[47] << 8);
6660   level->time_light             = header[48] | (header[49] << 8);
6661   level->time_timegate          = header[50] | (header[51] << 8);
6662   level->time_wheel             = header[52] | (header[53] << 8);
6663   level->time_magic_wall        = header[54] | (header[55] << 8);
6664   level->extra_time             = header[56] | (header[57] << 8);
6665   level->shield_normal_time     = header[58] | (header[59] << 8);
6666
6667   // shield and extra time elements do not have a score
6668   level->score[SC_SHIELD]       = 0;
6669   level->extra_time_score       = 0;
6670
6671   // set time for normal and deadly shields to the same value
6672   level->shield_deadly_time     = level->shield_normal_time;
6673
6674   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6675   // can slip down from flat walls, like normal walls and steel walls
6676   level->em_slippery_gems = TRUE;
6677
6678   // time score is counted for each 10 seconds left in Diamond Caves levels
6679   level->time_score_base = 10;
6680 }
6681
6682 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6683                                      struct LevelFileInfo *level_file_info,
6684                                      boolean level_info_only)
6685 {
6686   char *filename = level_file_info->filename;
6687   File *file;
6688   int num_magic_bytes = 8;
6689   char magic_bytes[num_magic_bytes + 1];
6690   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6691
6692   if (!(file = openFile(filename, MODE_READ)))
6693   {
6694     level->no_valid_file = TRUE;
6695
6696     if (!level_info_only)
6697       Warn("cannot read level '%s' -- using empty level", filename);
6698
6699     return;
6700   }
6701
6702   // fseek(file, 0x0000, SEEK_SET);
6703
6704   if (level_file_info->packed)
6705   {
6706     // read "magic bytes" from start of file
6707     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6708       magic_bytes[0] = '\0';
6709
6710     // check "magic bytes" for correct file format
6711     if (!strPrefix(magic_bytes, "DC2"))
6712     {
6713       level->no_valid_file = TRUE;
6714
6715       Warn("unknown DC level file '%s' -- using empty level", filename);
6716
6717       return;
6718     }
6719
6720     if (strPrefix(magic_bytes, "DC2Win95") ||
6721         strPrefix(magic_bytes, "DC2Win98"))
6722     {
6723       int position_first_level = 0x00fa;
6724       int extra_bytes = 4;
6725       int skip_bytes;
6726
6727       // advance file stream to first level inside the level package
6728       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6729
6730       // each block of level data is followed by block of non-level data
6731       num_levels_to_skip *= 2;
6732
6733       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6734       while (num_levels_to_skip >= 0)
6735       {
6736         // advance file stream to next level inside the level package
6737         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6738         {
6739           level->no_valid_file = TRUE;
6740
6741           Warn("cannot fseek in file '%s' -- using empty level", filename);
6742
6743           return;
6744         }
6745
6746         // skip apparently unused extra bytes following each level
6747         ReadUnusedBytesFromFile(file, extra_bytes);
6748
6749         // read size of next level in level package
6750         skip_bytes = getFile32BitLE(file);
6751
6752         num_levels_to_skip--;
6753       }
6754     }
6755     else
6756     {
6757       level->no_valid_file = TRUE;
6758
6759       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6760
6761       return;
6762     }
6763   }
6764
6765   LoadLevelFromFileStream_DC(file, level);
6766
6767   closeFile(file);
6768 }
6769
6770
6771 // ----------------------------------------------------------------------------
6772 // functions for loading SB level
6773 // ----------------------------------------------------------------------------
6774
6775 int getMappedElement_SB(int element_ascii, boolean use_ces)
6776 {
6777   static struct
6778   {
6779     int ascii;
6780     int sb;
6781     int ce;
6782   }
6783   sb_element_mapping[] =
6784   {
6785     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6786     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6787     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6788     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6789     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6790     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6791     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6792     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6793
6794     { 0,   -1,                      -1          },
6795   };
6796
6797   int i;
6798
6799   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6800     if (element_ascii == sb_element_mapping[i].ascii)
6801       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6802
6803   return EL_UNDEFINED;
6804 }
6805
6806 static void SetLevelSettings_SB(struct LevelInfo *level)
6807 {
6808   // time settings
6809   level->time = 0;
6810   level->use_step_counter = TRUE;
6811
6812   // score settings
6813   level->score[SC_TIME_BONUS] = 0;
6814   level->time_score_base = 1;
6815   level->rate_time_over_score = TRUE;
6816
6817   // game settings
6818   level->auto_exit_sokoban = TRUE;
6819 }
6820
6821 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6822                                      struct LevelFileInfo *level_file_info,
6823                                      boolean level_info_only)
6824 {
6825   char *filename = level_file_info->filename;
6826   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6827   char last_comment[MAX_LINE_LEN];
6828   char level_name[MAX_LINE_LEN];
6829   char *line_ptr;
6830   File *file;
6831   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6832   boolean read_continued_line = FALSE;
6833   boolean reading_playfield = FALSE;
6834   boolean got_valid_playfield_line = FALSE;
6835   boolean invalid_playfield_char = FALSE;
6836   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6837   int file_level_nr = 0;
6838   int x = 0, y = 0;             // initialized to make compilers happy
6839
6840   last_comment[0] = '\0';
6841   level_name[0] = '\0';
6842
6843   if (!(file = openFile(filename, MODE_READ)))
6844   {
6845     level->no_valid_file = TRUE;
6846
6847     if (!level_info_only)
6848       Warn("cannot read level '%s' -- using empty level", filename);
6849
6850     return;
6851   }
6852
6853   while (!checkEndOfFile(file))
6854   {
6855     // level successfully read, but next level may follow here
6856     if (!got_valid_playfield_line && reading_playfield)
6857     {
6858       // read playfield from single level file -- skip remaining file
6859       if (!level_file_info->packed)
6860         break;
6861
6862       if (file_level_nr >= num_levels_to_skip)
6863         break;
6864
6865       file_level_nr++;
6866
6867       last_comment[0] = '\0';
6868       level_name[0] = '\0';
6869
6870       reading_playfield = FALSE;
6871     }
6872
6873     got_valid_playfield_line = FALSE;
6874
6875     // read next line of input file
6876     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6877       break;
6878
6879     // cut trailing line break (this can be newline and/or carriage return)
6880     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6881       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6882         *line_ptr = '\0';
6883
6884     // copy raw input line for later use (mainly debugging output)
6885     strcpy(line_raw, line);
6886
6887     if (read_continued_line)
6888     {
6889       // append new line to existing line, if there is enough space
6890       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6891         strcat(previous_line, line_ptr);
6892
6893       strcpy(line, previous_line);      // copy storage buffer to line
6894
6895       read_continued_line = FALSE;
6896     }
6897
6898     // if the last character is '\', continue at next line
6899     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6900     {
6901       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6902       strcpy(previous_line, line);      // copy line to storage buffer
6903
6904       read_continued_line = TRUE;
6905
6906       continue;
6907     }
6908
6909     // skip empty lines
6910     if (line[0] == '\0')
6911       continue;
6912
6913     // extract comment text from comment line
6914     if (line[0] == ';')
6915     {
6916       for (line_ptr = line; *line_ptr; line_ptr++)
6917         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6918           break;
6919
6920       strcpy(last_comment, line_ptr);
6921
6922       continue;
6923     }
6924
6925     // extract level title text from line containing level title
6926     if (line[0] == '\'')
6927     {
6928       strcpy(level_name, &line[1]);
6929
6930       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6931         level_name[strlen(level_name) - 1] = '\0';
6932
6933       continue;
6934     }
6935
6936     // skip lines containing only spaces (or empty lines)
6937     for (line_ptr = line; *line_ptr; line_ptr++)
6938       if (*line_ptr != ' ')
6939         break;
6940     if (*line_ptr == '\0')
6941       continue;
6942
6943     // at this point, we have found a line containing part of a playfield
6944
6945     got_valid_playfield_line = TRUE;
6946
6947     if (!reading_playfield)
6948     {
6949       reading_playfield = TRUE;
6950       invalid_playfield_char = FALSE;
6951
6952       for (x = 0; x < MAX_LEV_FIELDX; x++)
6953         for (y = 0; y < MAX_LEV_FIELDY; y++)
6954           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6955
6956       level->fieldx = 0;
6957       level->fieldy = 0;
6958
6959       // start with topmost tile row
6960       y = 0;
6961     }
6962
6963     // skip playfield line if larger row than allowed
6964     if (y >= MAX_LEV_FIELDY)
6965       continue;
6966
6967     // start with leftmost tile column
6968     x = 0;
6969
6970     // read playfield elements from line
6971     for (line_ptr = line; *line_ptr; line_ptr++)
6972     {
6973       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6974
6975       // stop parsing playfield line if larger column than allowed
6976       if (x >= MAX_LEV_FIELDX)
6977         break;
6978
6979       if (mapped_sb_element == EL_UNDEFINED)
6980       {
6981         invalid_playfield_char = TRUE;
6982
6983         break;
6984       }
6985
6986       level->field[x][y] = mapped_sb_element;
6987
6988       // continue with next tile column
6989       x++;
6990
6991       level->fieldx = MAX(x, level->fieldx);
6992     }
6993
6994     if (invalid_playfield_char)
6995     {
6996       // if first playfield line, treat invalid lines as comment lines
6997       if (y == 0)
6998         reading_playfield = FALSE;
6999
7000       continue;
7001     }
7002
7003     // continue with next tile row
7004     y++;
7005   }
7006
7007   closeFile(file);
7008
7009   level->fieldy = y;
7010
7011   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7012   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7013
7014   if (!reading_playfield)
7015   {
7016     level->no_valid_file = TRUE;
7017
7018     Warn("cannot read level '%s' -- using empty level", filename);
7019
7020     return;
7021   }
7022
7023   if (*level_name != '\0')
7024   {
7025     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7026     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7027   }
7028   else if (*last_comment != '\0')
7029   {
7030     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7031     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7032   }
7033   else
7034   {
7035     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7036   }
7037
7038   // set all empty fields beyond the border walls to invisible steel wall
7039   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7040   {
7041     if ((x == 0 || x == level->fieldx - 1 ||
7042          y == 0 || y == level->fieldy - 1) &&
7043         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7044       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7045                      level->field, level->fieldx, level->fieldy);
7046   }
7047
7048   // set special level settings for Sokoban levels
7049   SetLevelSettings_SB(level);
7050
7051   if (load_xsb_to_ces)
7052   {
7053     // special global settings can now be set in level template
7054     level->use_custom_template = TRUE;
7055   }
7056 }
7057
7058
7059 // -------------------------------------------------------------------------
7060 // functions for handling native levels
7061 // -------------------------------------------------------------------------
7062
7063 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7064                                      struct LevelFileInfo *level_file_info,
7065                                      boolean level_info_only)
7066 {
7067   int pos = 0;
7068
7069   // determine position of requested level inside level package
7070   if (level_file_info->packed)
7071     pos = level_file_info->nr - leveldir_current->first_level;
7072
7073   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7074     level->no_valid_file = TRUE;
7075 }
7076
7077 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7078                                      struct LevelFileInfo *level_file_info,
7079                                      boolean level_info_only)
7080 {
7081   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7082     level->no_valid_file = TRUE;
7083 }
7084
7085 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7086                                      struct LevelFileInfo *level_file_info,
7087                                      boolean level_info_only)
7088 {
7089   int pos = 0;
7090
7091   // determine position of requested level inside level package
7092   if (level_file_info->packed)
7093     pos = level_file_info->nr - leveldir_current->first_level;
7094
7095   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7096     level->no_valid_file = TRUE;
7097 }
7098
7099 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7100                                      struct LevelFileInfo *level_file_info,
7101                                      boolean level_info_only)
7102 {
7103   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7104     level->no_valid_file = TRUE;
7105 }
7106
7107 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7108 {
7109   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7110     CopyNativeLevel_RND_to_BD(level);
7111   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7112     CopyNativeLevel_RND_to_EM(level);
7113   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7114     CopyNativeLevel_RND_to_SP(level);
7115   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7116     CopyNativeLevel_RND_to_MM(level);
7117 }
7118
7119 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7120 {
7121   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7122     CopyNativeLevel_BD_to_RND(level);
7123   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7124     CopyNativeLevel_EM_to_RND(level);
7125   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7126     CopyNativeLevel_SP_to_RND(level);
7127   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7128     CopyNativeLevel_MM_to_RND(level);
7129 }
7130
7131 void SaveNativeLevel(struct LevelInfo *level)
7132 {
7133   // saving native level files only supported for some game engines
7134   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7135       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7136     return;
7137
7138   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7139                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7140   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7141   char *filename = getLevelFilenameFromBasename(basename);
7142
7143   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7144     return;
7145
7146   boolean success = FALSE;
7147
7148   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7149   {
7150     CopyNativeLevel_RND_to_BD(level);
7151     // CopyNativeTape_RND_to_BD(level);
7152
7153     success = SaveNativeLevel_BD(filename);
7154   }
7155   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7156   {
7157     CopyNativeLevel_RND_to_SP(level);
7158     CopyNativeTape_RND_to_SP(level);
7159
7160     success = SaveNativeLevel_SP(filename);
7161   }
7162
7163   if (success)
7164     Request("Native level file saved!", REQ_CONFIRM);
7165   else
7166     Request("Failed to save native level file!", REQ_CONFIRM);
7167 }
7168
7169
7170 // ----------------------------------------------------------------------------
7171 // functions for loading generic level
7172 // ----------------------------------------------------------------------------
7173
7174 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7175                                   struct LevelFileInfo *level_file_info,
7176                                   boolean level_info_only)
7177 {
7178   // always start with reliable default values
7179   setLevelInfoToDefaults(level, level_info_only, TRUE);
7180
7181   switch (level_file_info->type)
7182   {
7183     case LEVEL_FILE_TYPE_RND:
7184       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7185       break;
7186
7187     case LEVEL_FILE_TYPE_BD:
7188       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7189       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7190       break;
7191
7192     case LEVEL_FILE_TYPE_EM:
7193       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7194       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7195       break;
7196
7197     case LEVEL_FILE_TYPE_SP:
7198       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7199       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7200       break;
7201
7202     case LEVEL_FILE_TYPE_MM:
7203       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7204       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7205       break;
7206
7207     case LEVEL_FILE_TYPE_DC:
7208       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7209       break;
7210
7211     case LEVEL_FILE_TYPE_SB:
7212       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7213       break;
7214
7215     default:
7216       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7217       break;
7218   }
7219
7220   // if level file is invalid, restore level structure to default values
7221   if (level->no_valid_file)
7222     setLevelInfoToDefaults(level, level_info_only, FALSE);
7223
7224   if (check_special_flags("use_native_bd_game_engine"))
7225     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7226
7227   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7228     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7229
7230   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7231     CopyNativeLevel_Native_to_RND(level);
7232 }
7233
7234 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7235 {
7236   static struct LevelFileInfo level_file_info;
7237
7238   // always start with reliable default values
7239   setFileInfoToDefaults(&level_file_info);
7240
7241   level_file_info.nr = 0;                       // unknown level number
7242   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7243
7244   setString(&level_file_info.filename, filename);
7245
7246   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7247 }
7248
7249 static void LoadLevel_InitVersion(struct LevelInfo *level)
7250 {
7251   int i, j;
7252
7253   if (leveldir_current == NULL)         // only when dumping level
7254     return;
7255
7256   // all engine modifications also valid for levels which use latest engine
7257   if (level->game_version < VERSION_IDENT(3,2,0,5))
7258   {
7259     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7260     level->time_score_base = 10;
7261   }
7262
7263   if (leveldir_current->latest_engine)
7264   {
7265     // ---------- use latest game engine --------------------------------------
7266
7267     /* For all levels which are forced to use the latest game engine version
7268        (normally all but user contributed, private and undefined levels), set
7269        the game engine version to the actual version; this allows for actual
7270        corrections in the game engine to take effect for existing, converted
7271        levels (from "classic" or other existing games) to make the emulation
7272        of the corresponding game more accurate, while (hopefully) not breaking
7273        existing levels created from other players. */
7274
7275     level->game_version = GAME_VERSION_ACTUAL;
7276
7277     /* Set special EM style gems behaviour: EM style gems slip down from
7278        normal, steel and growing wall. As this is a more fundamental change,
7279        it seems better to set the default behaviour to "off" (as it is more
7280        natural) and make it configurable in the level editor (as a property
7281        of gem style elements). Already existing converted levels (neither
7282        private nor contributed levels) are changed to the new behaviour. */
7283
7284     if (level->file_version < FILE_VERSION_2_0)
7285       level->em_slippery_gems = TRUE;
7286
7287     return;
7288   }
7289
7290   // ---------- use game engine the level was created with --------------------
7291
7292   /* For all levels which are not forced to use the latest game engine
7293      version (normally user contributed, private and undefined levels),
7294      use the version of the game engine the levels were created for.
7295
7296      Since 2.0.1, the game engine version is now directly stored
7297      in the level file (chunk "VERS"), so there is no need anymore
7298      to set the game version from the file version (except for old,
7299      pre-2.0 levels, where the game version is still taken from the
7300      file format version used to store the level -- see above). */
7301
7302   // player was faster than enemies in 1.0.0 and before
7303   if (level->file_version == FILE_VERSION_1_0)
7304     for (i = 0; i < MAX_PLAYERS; i++)
7305       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7306
7307   // default behaviour for EM style gems was "slippery" only in 2.0.1
7308   if (level->game_version == VERSION_IDENT(2,0,1,0))
7309     level->em_slippery_gems = TRUE;
7310
7311   // springs could be pushed over pits before (pre-release version) 2.2.0
7312   if (level->game_version < VERSION_IDENT(2,2,0,0))
7313     level->use_spring_bug = TRUE;
7314
7315   if (level->game_version < VERSION_IDENT(3,2,0,5))
7316   {
7317     // time orb caused limited time in endless time levels before 3.2.0-5
7318     level->use_time_orb_bug = TRUE;
7319
7320     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7321     level->block_snap_field = FALSE;
7322
7323     // extra time score was same value as time left score before 3.2.0-5
7324     level->extra_time_score = level->score[SC_TIME_BONUS];
7325   }
7326
7327   if (level->game_version < VERSION_IDENT(3,2,0,7))
7328   {
7329     // default behaviour for snapping was "not continuous" before 3.2.0-7
7330     level->continuous_snapping = FALSE;
7331   }
7332
7333   // only few elements were able to actively move into acid before 3.1.0
7334   // trigger settings did not exist before 3.1.0; set to default "any"
7335   if (level->game_version < VERSION_IDENT(3,1,0,0))
7336   {
7337     // correct "can move into acid" settings (all zero in old levels)
7338
7339     level->can_move_into_acid_bits = 0; // nothing can move into acid
7340     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7341
7342     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7343     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7344     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7345     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7346
7347     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7348       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7349
7350     // correct trigger settings (stored as zero == "none" in old levels)
7351
7352     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7353     {
7354       int element = EL_CUSTOM_START + i;
7355       struct ElementInfo *ei = &element_info[element];
7356
7357       for (j = 0; j < ei->num_change_pages; j++)
7358       {
7359         struct ElementChangeInfo *change = &ei->change_page[j];
7360
7361         change->trigger_player = CH_PLAYER_ANY;
7362         change->trigger_page = CH_PAGE_ANY;
7363       }
7364     }
7365   }
7366
7367   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7368   {
7369     int element = EL_CUSTOM_256;
7370     struct ElementInfo *ei = &element_info[element];
7371     struct ElementChangeInfo *change = &ei->change_page[0];
7372
7373     /* This is needed to fix a problem that was caused by a bugfix in function
7374        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7375        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7376        not replace walkable elements, but instead just placed the player on it,
7377        without placing the Sokoban field under the player). Unfortunately, this
7378        breaks "Snake Bite" style levels when the snake is halfway through a door
7379        that just closes (the snake head is still alive and can be moved in this
7380        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7381        player (without Sokoban element) which then gets killed as designed). */
7382
7383     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7384          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7385         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7386       change->target_element = EL_PLAYER_1;
7387   }
7388
7389   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7390   if (level->game_version < VERSION_IDENT(3,2,5,0))
7391   {
7392     /* This is needed to fix a problem that was caused by a bugfix in function
7393        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7394        corrects the behaviour when a custom element changes to another custom
7395        element with a higher element number that has change actions defined.
7396        Normally, only one change per frame is allowed for custom elements.
7397        Therefore, it is checked if a custom element already changed in the
7398        current frame; if it did, subsequent changes are suppressed.
7399        Unfortunately, this is only checked for element changes, but not for
7400        change actions, which are still executed. As the function above loops
7401        through all custom elements from lower to higher, an element change
7402        resulting in a lower CE number won't be checked again, while a target
7403        element with a higher number will also be checked, and potential change
7404        actions will get executed for this CE, too (which is wrong), while
7405        further changes are ignored (which is correct). As this bugfix breaks
7406        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7407        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7408        behaviour for existing levels and tapes that make use of this bug */
7409
7410     level->use_action_after_change_bug = TRUE;
7411   }
7412
7413   // not centering level after relocating player was default only in 3.2.3
7414   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7415     level->shifted_relocation = TRUE;
7416
7417   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7418   if (level->game_version < VERSION_IDENT(3,2,6,0))
7419     level->em_explodes_by_fire = TRUE;
7420
7421   // levels were solved by the first player entering an exit up to 4.1.0.0
7422   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7423     level->solved_by_one_player = TRUE;
7424
7425   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7426   if (level->game_version < VERSION_IDENT(4,1,1,1))
7427     level->use_life_bugs = TRUE;
7428
7429   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7430   if (level->game_version < VERSION_IDENT(4,1,1,1))
7431     level->sb_objects_needed = FALSE;
7432
7433   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7434   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7435     level->finish_dig_collect = FALSE;
7436
7437   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7438   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7439     level->keep_walkable_ce = TRUE;
7440 }
7441
7442 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7443 {
7444   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7445   int x, y;
7446
7447   // check if this level is (not) a Sokoban level
7448   for (y = 0; y < level->fieldy; y++)
7449     for (x = 0; x < level->fieldx; x++)
7450       if (!IS_SB_ELEMENT(Tile[x][y]))
7451         is_sokoban_level = FALSE;
7452
7453   if (is_sokoban_level)
7454   {
7455     // set special level settings for Sokoban levels
7456     SetLevelSettings_SB(level);
7457   }
7458 }
7459
7460 static void LoadLevel_InitSettings(struct LevelInfo *level)
7461 {
7462   // adjust level settings for (non-native) Sokoban-style levels
7463   LoadLevel_InitSettings_SB(level);
7464
7465   // rename levels with title "nameless level" or if renaming is forced
7466   if (leveldir_current->empty_level_name != NULL &&
7467       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7468        leveldir_current->force_level_name))
7469     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7470              leveldir_current->empty_level_name, level_nr);
7471 }
7472
7473 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7474 {
7475   int i, x, y;
7476
7477   // map elements that have changed in newer versions
7478   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7479                                                     level->game_version);
7480   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7481     for (x = 0; x < 3; x++)
7482       for (y = 0; y < 3; y++)
7483         level->yamyam_content[i].e[x][y] =
7484           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7485                                     level->game_version);
7486
7487 }
7488
7489 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7490 {
7491   int i, j;
7492
7493   // map custom element change events that have changed in newer versions
7494   // (these following values were accidentally changed in version 3.0.1)
7495   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7496   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7497   {
7498     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7499     {
7500       int element = EL_CUSTOM_START + i;
7501
7502       // order of checking and copying events to be mapped is important
7503       // (do not change the start and end value -- they are constant)
7504       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7505       {
7506         if (HAS_CHANGE_EVENT(element, j - 2))
7507         {
7508           SET_CHANGE_EVENT(element, j - 2, FALSE);
7509           SET_CHANGE_EVENT(element, j, TRUE);
7510         }
7511       }
7512
7513       // order of checking and copying events to be mapped is important
7514       // (do not change the start and end value -- they are constant)
7515       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7516       {
7517         if (HAS_CHANGE_EVENT(element, j - 1))
7518         {
7519           SET_CHANGE_EVENT(element, j - 1, FALSE);
7520           SET_CHANGE_EVENT(element, j, TRUE);
7521         }
7522       }
7523     }
7524   }
7525
7526   // initialize "can_change" field for old levels with only one change page
7527   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7528   {
7529     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7530     {
7531       int element = EL_CUSTOM_START + i;
7532
7533       if (CAN_CHANGE(element))
7534         element_info[element].change->can_change = TRUE;
7535     }
7536   }
7537
7538   // correct custom element values (for old levels without these options)
7539   if (level->game_version < VERSION_IDENT(3,1,1,0))
7540   {
7541     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7542     {
7543       int element = EL_CUSTOM_START + i;
7544       struct ElementInfo *ei = &element_info[element];
7545
7546       if (ei->access_direction == MV_NO_DIRECTION)
7547         ei->access_direction = MV_ALL_DIRECTIONS;
7548     }
7549   }
7550
7551   // correct custom element values (fix invalid values for all versions)
7552   if (1)
7553   {
7554     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7555     {
7556       int element = EL_CUSTOM_START + i;
7557       struct ElementInfo *ei = &element_info[element];
7558
7559       for (j = 0; j < ei->num_change_pages; j++)
7560       {
7561         struct ElementChangeInfo *change = &ei->change_page[j];
7562
7563         if (change->trigger_player == CH_PLAYER_NONE)
7564           change->trigger_player = CH_PLAYER_ANY;
7565
7566         if (change->trigger_side == CH_SIDE_NONE)
7567           change->trigger_side = CH_SIDE_ANY;
7568       }
7569     }
7570   }
7571
7572   // initialize "can_explode" field for old levels which did not store this
7573   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7574   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7575   {
7576     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7577     {
7578       int element = EL_CUSTOM_START + i;
7579
7580       if (EXPLODES_1X1_OLD(element))
7581         element_info[element].explosion_type = EXPLODES_1X1;
7582
7583       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7584                                              EXPLODES_SMASHED(element) ||
7585                                              EXPLODES_IMPACT(element)));
7586     }
7587   }
7588
7589   // correct previously hard-coded move delay values for maze runner style
7590   if (level->game_version < VERSION_IDENT(3,1,1,0))
7591   {
7592     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7593     {
7594       int element = EL_CUSTOM_START + i;
7595
7596       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7597       {
7598         // previously hard-coded and therefore ignored
7599         element_info[element].move_delay_fixed = 9;
7600         element_info[element].move_delay_random = 0;
7601       }
7602     }
7603   }
7604
7605   // set some other uninitialized values of custom elements in older levels
7606   if (level->game_version < VERSION_IDENT(3,1,0,0))
7607   {
7608     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7609     {
7610       int element = EL_CUSTOM_START + i;
7611
7612       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7613
7614       element_info[element].explosion_delay = 17;
7615       element_info[element].ignition_delay = 8;
7616     }
7617   }
7618
7619   // set mouse click change events to work for left/middle/right mouse button
7620   if (level->game_version < VERSION_IDENT(4,2,3,0))
7621   {
7622     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7623     {
7624       int element = EL_CUSTOM_START + i;
7625       struct ElementInfo *ei = &element_info[element];
7626
7627       for (j = 0; j < ei->num_change_pages; j++)
7628       {
7629         struct ElementChangeInfo *change = &ei->change_page[j];
7630
7631         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7632             change->has_event[CE_PRESSED_BY_MOUSE] ||
7633             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7634             change->has_event[CE_MOUSE_PRESSED_ON_X])
7635           change->trigger_side = CH_SIDE_ANY;
7636       }
7637     }
7638   }
7639 }
7640
7641 static void LoadLevel_InitElements(struct LevelInfo *level)
7642 {
7643   LoadLevel_InitStandardElements(level);
7644
7645   if (level->file_has_custom_elements)
7646     LoadLevel_InitCustomElements(level);
7647
7648   // initialize element properties for level editor etc.
7649   InitElementPropertiesEngine(level->game_version);
7650   InitElementPropertiesGfxElement();
7651 }
7652
7653 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7654 {
7655   int x, y;
7656
7657   // map elements that have changed in newer versions
7658   for (y = 0; y < level->fieldy; y++)
7659     for (x = 0; x < level->fieldx; x++)
7660       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7661                                                      level->game_version);
7662
7663   // clear unused playfield data (nicer if level gets resized in editor)
7664   for (x = 0; x < MAX_LEV_FIELDX; x++)
7665     for (y = 0; y < MAX_LEV_FIELDY; y++)
7666       if (x >= level->fieldx || y >= level->fieldy)
7667         level->field[x][y] = EL_EMPTY;
7668
7669   // copy elements to runtime playfield array
7670   for (x = 0; x < MAX_LEV_FIELDX; x++)
7671     for (y = 0; y < MAX_LEV_FIELDY; y++)
7672       Tile[x][y] = level->field[x][y];
7673
7674   // initialize level size variables for faster access
7675   lev_fieldx = level->fieldx;
7676   lev_fieldy = level->fieldy;
7677
7678   // determine border element for this level
7679   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7680     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7681   else
7682     SetBorderElement();
7683 }
7684
7685 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7686 {
7687   struct LevelFileInfo *level_file_info = &level->file_info;
7688
7689   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7690     CopyNativeLevel_RND_to_Native(level);
7691 }
7692
7693 static void LoadLevelTemplate_LoadAndInit(void)
7694 {
7695   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7696
7697   LoadLevel_InitVersion(&level_template);
7698   LoadLevel_InitElements(&level_template);
7699   LoadLevel_InitSettings(&level_template);
7700
7701   ActivateLevelTemplate();
7702 }
7703
7704 void LoadLevelTemplate(int nr)
7705 {
7706   if (!fileExists(getGlobalLevelTemplateFilename()))
7707   {
7708     Warn("no level template found for this level");
7709
7710     return;
7711   }
7712
7713   setLevelFileInfo(&level_template.file_info, nr);
7714
7715   LoadLevelTemplate_LoadAndInit();
7716 }
7717
7718 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7719 {
7720   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7721
7722   LoadLevelTemplate_LoadAndInit();
7723 }
7724
7725 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7726 {
7727   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7728
7729   if (level.use_custom_template)
7730   {
7731     if (network_level != NULL)
7732       LoadNetworkLevelTemplate(network_level);
7733     else
7734       LoadLevelTemplate(-1);
7735   }
7736
7737   LoadLevel_InitVersion(&level);
7738   LoadLevel_InitElements(&level);
7739   LoadLevel_InitPlayfield(&level);
7740   LoadLevel_InitSettings(&level);
7741
7742   LoadLevel_InitNativeEngines(&level);
7743 }
7744
7745 void LoadLevel(int nr)
7746 {
7747   SetLevelSetInfo(leveldir_current->identifier, nr);
7748
7749   setLevelFileInfo(&level.file_info, nr);
7750
7751   LoadLevel_LoadAndInit(NULL);
7752 }
7753
7754 void LoadLevelInfoOnly(int nr)
7755 {
7756   setLevelFileInfo(&level.file_info, nr);
7757
7758   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7759 }
7760
7761 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7762 {
7763   SetLevelSetInfo(network_level->leveldir_identifier,
7764                   network_level->file_info.nr);
7765
7766   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7767
7768   LoadLevel_LoadAndInit(network_level);
7769 }
7770
7771 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7772 {
7773   int chunk_size = 0;
7774
7775   chunk_size += putFileVersion(file, level->file_version);
7776   chunk_size += putFileVersion(file, level->game_version);
7777
7778   return chunk_size;
7779 }
7780
7781 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7782 {
7783   int chunk_size = 0;
7784
7785   chunk_size += putFile16BitBE(file, level->creation_date.year);
7786   chunk_size += putFile8Bit(file,    level->creation_date.month);
7787   chunk_size += putFile8Bit(file,    level->creation_date.day);
7788
7789   return chunk_size;
7790 }
7791
7792 #if ENABLE_HISTORIC_CHUNKS
7793 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7794 {
7795   int i, x, y;
7796
7797   putFile8Bit(file, level->fieldx);
7798   putFile8Bit(file, level->fieldy);
7799
7800   putFile16BitBE(file, level->time);
7801   putFile16BitBE(file, level->gems_needed);
7802
7803   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7804     putFile8Bit(file, level->name[i]);
7805
7806   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7807     putFile8Bit(file, level->score[i]);
7808
7809   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7810     for (y = 0; y < 3; y++)
7811       for (x = 0; x < 3; x++)
7812         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7813                            level->yamyam_content[i].e[x][y]));
7814   putFile8Bit(file, level->amoeba_speed);
7815   putFile8Bit(file, level->time_magic_wall);
7816   putFile8Bit(file, level->time_wheel);
7817   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7818                      level->amoeba_content));
7819   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7820   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7821   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7822   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7823
7824   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7825
7826   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7827   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7828   putFile32BitBE(file, level->can_move_into_acid_bits);
7829   putFile8Bit(file, level->dont_collide_with_bits);
7830
7831   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7832   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7833
7834   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7835   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7836   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7837
7838   putFile8Bit(file, level->game_engine_type);
7839
7840   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7841 }
7842 #endif
7843
7844 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7845 {
7846   int chunk_size = 0;
7847   int i;
7848
7849   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7850     chunk_size += putFile8Bit(file, level->name[i]);
7851
7852   return chunk_size;
7853 }
7854
7855 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7856 {
7857   int chunk_size = 0;
7858   int i;
7859
7860   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7861     chunk_size += putFile8Bit(file, level->author[i]);
7862
7863   return chunk_size;
7864 }
7865
7866 #if ENABLE_HISTORIC_CHUNKS
7867 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7868 {
7869   int chunk_size = 0;
7870   int x, y;
7871
7872   for (y = 0; y < level->fieldy; y++)
7873     for (x = 0; x < level->fieldx; x++)
7874       if (level->encoding_16bit_field)
7875         chunk_size += putFile16BitBE(file, level->field[x][y]);
7876       else
7877         chunk_size += putFile8Bit(file, level->field[x][y]);
7878
7879   return chunk_size;
7880 }
7881 #endif
7882
7883 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7884 {
7885   int chunk_size = 0;
7886   int x, y;
7887
7888   for (y = 0; y < level->fieldy; y++) 
7889     for (x = 0; x < level->fieldx; x++) 
7890       chunk_size += putFile16BitBE(file, level->field[x][y]);
7891
7892   return chunk_size;
7893 }
7894
7895 #if ENABLE_HISTORIC_CHUNKS
7896 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7897 {
7898   int i, x, y;
7899
7900   putFile8Bit(file, EL_YAMYAM);
7901   putFile8Bit(file, level->num_yamyam_contents);
7902   putFile8Bit(file, 0);
7903   putFile8Bit(file, 0);
7904
7905   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7906     for (y = 0; y < 3; y++)
7907       for (x = 0; x < 3; x++)
7908         if (level->encoding_16bit_field)
7909           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7910         else
7911           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7912 }
7913 #endif
7914
7915 #if ENABLE_HISTORIC_CHUNKS
7916 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7917 {
7918   int i, x, y;
7919   int num_contents, content_xsize, content_ysize;
7920   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7921
7922   if (element == EL_YAMYAM)
7923   {
7924     num_contents = level->num_yamyam_contents;
7925     content_xsize = 3;
7926     content_ysize = 3;
7927
7928     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7929       for (y = 0; y < 3; y++)
7930         for (x = 0; x < 3; x++)
7931           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7932   }
7933   else if (element == EL_BD_AMOEBA)
7934   {
7935     num_contents = 1;
7936     content_xsize = 1;
7937     content_ysize = 1;
7938
7939     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7940       for (y = 0; y < 3; y++)
7941         for (x = 0; x < 3; x++)
7942           content_array[i][x][y] = EL_EMPTY;
7943     content_array[0][0][0] = level->amoeba_content;
7944   }
7945   else
7946   {
7947     // chunk header already written -- write empty chunk data
7948     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7949
7950     Warn("cannot save content for element '%d'", element);
7951
7952     return;
7953   }
7954
7955   putFile16BitBE(file, element);
7956   putFile8Bit(file, num_contents);
7957   putFile8Bit(file, content_xsize);
7958   putFile8Bit(file, content_ysize);
7959
7960   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7961
7962   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7963     for (y = 0; y < 3; y++)
7964       for (x = 0; x < 3; x++)
7965         putFile16BitBE(file, content_array[i][x][y]);
7966 }
7967 #endif
7968
7969 #if ENABLE_HISTORIC_CHUNKS
7970 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7971 {
7972   int envelope_nr = element - EL_ENVELOPE_1;
7973   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7974   int chunk_size = 0;
7975   int i;
7976
7977   chunk_size += putFile16BitBE(file, element);
7978   chunk_size += putFile16BitBE(file, envelope_len);
7979   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7980   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7981
7982   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7983   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7984
7985   for (i = 0; i < envelope_len; i++)
7986     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7987
7988   return chunk_size;
7989 }
7990 #endif
7991
7992 #if ENABLE_HISTORIC_CHUNKS
7993 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7994                            int num_changed_custom_elements)
7995 {
7996   int i, check = 0;
7997
7998   putFile16BitBE(file, num_changed_custom_elements);
7999
8000   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8001   {
8002     int element = EL_CUSTOM_START + i;
8003
8004     struct ElementInfo *ei = &element_info[element];
8005
8006     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8007     {
8008       if (check < num_changed_custom_elements)
8009       {
8010         putFile16BitBE(file, element);
8011         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8012       }
8013
8014       check++;
8015     }
8016   }
8017
8018   if (check != num_changed_custom_elements)     // should not happen
8019     Warn("inconsistent number of custom element properties");
8020 }
8021 #endif
8022
8023 #if ENABLE_HISTORIC_CHUNKS
8024 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8025                            int num_changed_custom_elements)
8026 {
8027   int i, check = 0;
8028
8029   putFile16BitBE(file, num_changed_custom_elements);
8030
8031   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8032   {
8033     int element = EL_CUSTOM_START + i;
8034
8035     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8036     {
8037       if (check < num_changed_custom_elements)
8038       {
8039         putFile16BitBE(file, element);
8040         putFile16BitBE(file, element_info[element].change->target_element);
8041       }
8042
8043       check++;
8044     }
8045   }
8046
8047   if (check != num_changed_custom_elements)     // should not happen
8048     Warn("inconsistent number of custom target elements");
8049 }
8050 #endif
8051
8052 #if ENABLE_HISTORIC_CHUNKS
8053 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8054                            int num_changed_custom_elements)
8055 {
8056   int i, j, x, y, check = 0;
8057
8058   putFile16BitBE(file, num_changed_custom_elements);
8059
8060   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8061   {
8062     int element = EL_CUSTOM_START + i;
8063     struct ElementInfo *ei = &element_info[element];
8064
8065     if (ei->modified_settings)
8066     {
8067       if (check < num_changed_custom_elements)
8068       {
8069         putFile16BitBE(file, element);
8070
8071         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8072           putFile8Bit(file, ei->description[j]);
8073
8074         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8075
8076         // some free bytes for future properties and padding
8077         WriteUnusedBytesToFile(file, 7);
8078
8079         putFile8Bit(file, ei->use_gfx_element);
8080         putFile16BitBE(file, ei->gfx_element_initial);
8081
8082         putFile8Bit(file, ei->collect_score_initial);
8083         putFile8Bit(file, ei->collect_count_initial);
8084
8085         putFile16BitBE(file, ei->push_delay_fixed);
8086         putFile16BitBE(file, ei->push_delay_random);
8087         putFile16BitBE(file, ei->move_delay_fixed);
8088         putFile16BitBE(file, ei->move_delay_random);
8089
8090         putFile16BitBE(file, ei->move_pattern);
8091         putFile8Bit(file, ei->move_direction_initial);
8092         putFile8Bit(file, ei->move_stepsize);
8093
8094         for (y = 0; y < 3; y++)
8095           for (x = 0; x < 3; x++)
8096             putFile16BitBE(file, ei->content.e[x][y]);
8097
8098         putFile32BitBE(file, ei->change->events);
8099
8100         putFile16BitBE(file, ei->change->target_element);
8101
8102         putFile16BitBE(file, ei->change->delay_fixed);
8103         putFile16BitBE(file, ei->change->delay_random);
8104         putFile16BitBE(file, ei->change->delay_frames);
8105
8106         putFile16BitBE(file, ei->change->initial_trigger_element);
8107
8108         putFile8Bit(file, ei->change->explode);
8109         putFile8Bit(file, ei->change->use_target_content);
8110         putFile8Bit(file, ei->change->only_if_complete);
8111         putFile8Bit(file, ei->change->use_random_replace);
8112
8113         putFile8Bit(file, ei->change->random_percentage);
8114         putFile8Bit(file, ei->change->replace_when);
8115
8116         for (y = 0; y < 3; y++)
8117           for (x = 0; x < 3; x++)
8118             putFile16BitBE(file, ei->change->content.e[x][y]);
8119
8120         putFile8Bit(file, ei->slippery_type);
8121
8122         // some free bytes for future properties and padding
8123         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8124       }
8125
8126       check++;
8127     }
8128   }
8129
8130   if (check != num_changed_custom_elements)     // should not happen
8131     Warn("inconsistent number of custom element properties");
8132 }
8133 #endif
8134
8135 #if ENABLE_HISTORIC_CHUNKS
8136 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8137 {
8138   struct ElementInfo *ei = &element_info[element];
8139   int i, j, x, y;
8140
8141   // ---------- custom element base property values (96 bytes) ----------------
8142
8143   putFile16BitBE(file, element);
8144
8145   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8146     putFile8Bit(file, ei->description[i]);
8147
8148   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8149
8150   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8151
8152   putFile8Bit(file, ei->num_change_pages);
8153
8154   putFile16BitBE(file, ei->ce_value_fixed_initial);
8155   putFile16BitBE(file, ei->ce_value_random_initial);
8156   putFile8Bit(file, ei->use_last_ce_value);
8157
8158   putFile8Bit(file, ei->use_gfx_element);
8159   putFile16BitBE(file, ei->gfx_element_initial);
8160
8161   putFile8Bit(file, ei->collect_score_initial);
8162   putFile8Bit(file, ei->collect_count_initial);
8163
8164   putFile8Bit(file, ei->drop_delay_fixed);
8165   putFile8Bit(file, ei->push_delay_fixed);
8166   putFile8Bit(file, ei->drop_delay_random);
8167   putFile8Bit(file, ei->push_delay_random);
8168   putFile16BitBE(file, ei->move_delay_fixed);
8169   putFile16BitBE(file, ei->move_delay_random);
8170
8171   // bits 0 - 15 of "move_pattern" ...
8172   putFile16BitBE(file, ei->move_pattern & 0xffff);
8173   putFile8Bit(file, ei->move_direction_initial);
8174   putFile8Bit(file, ei->move_stepsize);
8175
8176   putFile8Bit(file, ei->slippery_type);
8177
8178   for (y = 0; y < 3; y++)
8179     for (x = 0; x < 3; x++)
8180       putFile16BitBE(file, ei->content.e[x][y]);
8181
8182   putFile16BitBE(file, ei->move_enter_element);
8183   putFile16BitBE(file, ei->move_leave_element);
8184   putFile8Bit(file, ei->move_leave_type);
8185
8186   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8187   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8188
8189   putFile8Bit(file, ei->access_direction);
8190
8191   putFile8Bit(file, ei->explosion_delay);
8192   putFile8Bit(file, ei->ignition_delay);
8193   putFile8Bit(file, ei->explosion_type);
8194
8195   // some free bytes for future custom property values and padding
8196   WriteUnusedBytesToFile(file, 1);
8197
8198   // ---------- change page property values (48 bytes) ------------------------
8199
8200   for (i = 0; i < ei->num_change_pages; i++)
8201   {
8202     struct ElementChangeInfo *change = &ei->change_page[i];
8203     unsigned int event_bits;
8204
8205     // bits 0 - 31 of "has_event[]" ...
8206     event_bits = 0;
8207     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8208       if (change->has_event[j])
8209         event_bits |= (1u << j);
8210     putFile32BitBE(file, event_bits);
8211
8212     putFile16BitBE(file, change->target_element);
8213
8214     putFile16BitBE(file, change->delay_fixed);
8215     putFile16BitBE(file, change->delay_random);
8216     putFile16BitBE(file, change->delay_frames);
8217
8218     putFile16BitBE(file, change->initial_trigger_element);
8219
8220     putFile8Bit(file, change->explode);
8221     putFile8Bit(file, change->use_target_content);
8222     putFile8Bit(file, change->only_if_complete);
8223     putFile8Bit(file, change->use_random_replace);
8224
8225     putFile8Bit(file, change->random_percentage);
8226     putFile8Bit(file, change->replace_when);
8227
8228     for (y = 0; y < 3; y++)
8229       for (x = 0; x < 3; x++)
8230         putFile16BitBE(file, change->target_content.e[x][y]);
8231
8232     putFile8Bit(file, change->can_change);
8233
8234     putFile8Bit(file, change->trigger_side);
8235
8236     putFile8Bit(file, change->trigger_player);
8237     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8238                        log_2(change->trigger_page)));
8239
8240     putFile8Bit(file, change->has_action);
8241     putFile8Bit(file, change->action_type);
8242     putFile8Bit(file, change->action_mode);
8243     putFile16BitBE(file, change->action_arg);
8244
8245     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8246     event_bits = 0;
8247     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8248       if (change->has_event[j])
8249         event_bits |= (1u << (j - 32));
8250     putFile8Bit(file, event_bits);
8251   }
8252 }
8253 #endif
8254
8255 #if ENABLE_HISTORIC_CHUNKS
8256 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8257 {
8258   struct ElementInfo *ei = &element_info[element];
8259   struct ElementGroupInfo *group = ei->group;
8260   int i;
8261
8262   putFile16BitBE(file, element);
8263
8264   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8265     putFile8Bit(file, ei->description[i]);
8266
8267   putFile8Bit(file, group->num_elements);
8268
8269   putFile8Bit(file, ei->use_gfx_element);
8270   putFile16BitBE(file, ei->gfx_element_initial);
8271
8272   putFile8Bit(file, group->choice_mode);
8273
8274   // some free bytes for future values and padding
8275   WriteUnusedBytesToFile(file, 3);
8276
8277   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8278     putFile16BitBE(file, group->element[i]);
8279 }
8280 #endif
8281
8282 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8283                                 boolean write_element)
8284 {
8285   int save_type = entry->save_type;
8286   int data_type = entry->data_type;
8287   int conf_type = entry->conf_type;
8288   int byte_mask = conf_type & CONF_MASK_BYTES;
8289   int element = entry->element;
8290   int default_value = entry->default_value;
8291   int num_bytes = 0;
8292   boolean modified = FALSE;
8293
8294   if (byte_mask != CONF_MASK_MULTI_BYTES)
8295   {
8296     void *value_ptr = entry->value;
8297     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8298                  *(int *)value_ptr);
8299
8300     // check if any settings have been modified before saving them
8301     if (value != default_value)
8302       modified = TRUE;
8303
8304     // do not save if explicitly told or if unmodified default settings
8305     if ((save_type == SAVE_CONF_NEVER) ||
8306         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8307       return 0;
8308
8309     if (write_element)
8310       num_bytes += putFile16BitBE(file, element);
8311
8312     num_bytes += putFile8Bit(file, conf_type);
8313     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8314                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8315                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8316                   0);
8317   }
8318   else if (data_type == TYPE_STRING)
8319   {
8320     char *default_string = entry->default_string;
8321     char *string = (char *)(entry->value);
8322     int string_length = strlen(string);
8323     int i;
8324
8325     // check if any settings have been modified before saving them
8326     if (!strEqual(string, default_string))
8327       modified = TRUE;
8328
8329     // do not save if explicitly told or if unmodified default settings
8330     if ((save_type == SAVE_CONF_NEVER) ||
8331         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8332       return 0;
8333
8334     if (write_element)
8335       num_bytes += putFile16BitBE(file, element);
8336
8337     num_bytes += putFile8Bit(file, conf_type);
8338     num_bytes += putFile16BitBE(file, string_length);
8339
8340     for (i = 0; i < string_length; i++)
8341       num_bytes += putFile8Bit(file, string[i]);
8342   }
8343   else if (data_type == TYPE_ELEMENT_LIST)
8344   {
8345     int *element_array = (int *)(entry->value);
8346     int num_elements = *(int *)(entry->num_entities);
8347     int i;
8348
8349     // check if any settings have been modified before saving them
8350     for (i = 0; i < num_elements; i++)
8351       if (element_array[i] != default_value)
8352         modified = TRUE;
8353
8354     // do not save if explicitly told or if unmodified default settings
8355     if ((save_type == SAVE_CONF_NEVER) ||
8356         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8357       return 0;
8358
8359     if (write_element)
8360       num_bytes += putFile16BitBE(file, element);
8361
8362     num_bytes += putFile8Bit(file, conf_type);
8363     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8364
8365     for (i = 0; i < num_elements; i++)
8366       num_bytes += putFile16BitBE(file, element_array[i]);
8367   }
8368   else if (data_type == TYPE_CONTENT_LIST)
8369   {
8370     struct Content *content = (struct Content *)(entry->value);
8371     int num_contents = *(int *)(entry->num_entities);
8372     int i, x, y;
8373
8374     // check if any settings have been modified before saving them
8375     for (i = 0; i < num_contents; i++)
8376       for (y = 0; y < 3; y++)
8377         for (x = 0; x < 3; x++)
8378           if (content[i].e[x][y] != default_value)
8379             modified = TRUE;
8380
8381     // do not save if explicitly told or if unmodified default settings
8382     if ((save_type == SAVE_CONF_NEVER) ||
8383         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8384       return 0;
8385
8386     if (write_element)
8387       num_bytes += putFile16BitBE(file, element);
8388
8389     num_bytes += putFile8Bit(file, conf_type);
8390     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8391
8392     for (i = 0; i < num_contents; i++)
8393       for (y = 0; y < 3; y++)
8394         for (x = 0; x < 3; x++)
8395           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8396   }
8397
8398   return num_bytes;
8399 }
8400
8401 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8402 {
8403   int chunk_size = 0;
8404   int i;
8405
8406   li = *level;          // copy level data into temporary buffer
8407
8408   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8409     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8410
8411   return chunk_size;
8412 }
8413
8414 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8415 {
8416   int chunk_size = 0;
8417   int i;
8418
8419   li = *level;          // copy level data into temporary buffer
8420
8421   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8422     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8423
8424   return chunk_size;
8425 }
8426
8427 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8428 {
8429   int envelope_nr = element - EL_ENVELOPE_1;
8430   int chunk_size = 0;
8431   int i;
8432
8433   chunk_size += putFile16BitBE(file, element);
8434
8435   // copy envelope data into temporary buffer
8436   xx_envelope = level->envelope[envelope_nr];
8437
8438   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8439     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8440
8441   return chunk_size;
8442 }
8443
8444 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8445 {
8446   struct ElementInfo *ei = &element_info[element];
8447   int chunk_size = 0;
8448   int i, j;
8449
8450   chunk_size += putFile16BitBE(file, element);
8451
8452   xx_ei = *ei;          // copy element data into temporary buffer
8453
8454   // set default description string for this specific element
8455   strcpy(xx_default_description, getDefaultElementDescription(ei));
8456
8457   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8458     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8459
8460   for (i = 0; i < ei->num_change_pages; i++)
8461   {
8462     struct ElementChangeInfo *change = &ei->change_page[i];
8463
8464     xx_current_change_page = i;
8465
8466     xx_change = *change;        // copy change data into temporary buffer
8467
8468     resetEventBits();
8469     setEventBitsFromEventFlags(change);
8470
8471     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8472       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8473                                          FALSE);
8474   }
8475
8476   return chunk_size;
8477 }
8478
8479 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8480 {
8481   struct ElementInfo *ei = &element_info[element];
8482   struct ElementGroupInfo *group = ei->group;
8483   int chunk_size = 0;
8484   int i;
8485
8486   chunk_size += putFile16BitBE(file, element);
8487
8488   xx_ei = *ei;          // copy element data into temporary buffer
8489   xx_group = *group;    // copy group data into temporary buffer
8490
8491   // set default description string for this specific element
8492   strcpy(xx_default_description, getDefaultElementDescription(ei));
8493
8494   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8495     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8496
8497   return chunk_size;
8498 }
8499
8500 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8501 {
8502   struct ElementInfo *ei = &element_info[element];
8503   int chunk_size = 0;
8504   int i;
8505
8506   chunk_size += putFile16BitBE(file, element);
8507
8508   xx_ei = *ei;          // copy element data into temporary buffer
8509
8510   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8511     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8512
8513   return chunk_size;
8514 }
8515
8516 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8517                                   boolean save_as_template)
8518 {
8519   int chunk_size;
8520   int i;
8521   FILE *file;
8522
8523   if (!(file = fopen(filename, MODE_WRITE)))
8524   {
8525     Warn("cannot save level file '%s'", filename);
8526
8527     return;
8528   }
8529
8530   level->file_version = FILE_VERSION_ACTUAL;
8531   level->game_version = GAME_VERSION_ACTUAL;
8532
8533   level->creation_date = getCurrentDate();
8534
8535   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8536   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8537
8538   chunk_size = SaveLevel_VERS(NULL, level);
8539   putFileChunkBE(file, "VERS", chunk_size);
8540   SaveLevel_VERS(file, level);
8541
8542   chunk_size = SaveLevel_DATE(NULL, level);
8543   putFileChunkBE(file, "DATE", chunk_size);
8544   SaveLevel_DATE(file, level);
8545
8546   chunk_size = SaveLevel_NAME(NULL, level);
8547   putFileChunkBE(file, "NAME", chunk_size);
8548   SaveLevel_NAME(file, level);
8549
8550   chunk_size = SaveLevel_AUTH(NULL, level);
8551   putFileChunkBE(file, "AUTH", chunk_size);
8552   SaveLevel_AUTH(file, level);
8553
8554   chunk_size = SaveLevel_INFO(NULL, level);
8555   putFileChunkBE(file, "INFO", chunk_size);
8556   SaveLevel_INFO(file, level);
8557
8558   chunk_size = SaveLevel_BODY(NULL, level);
8559   putFileChunkBE(file, "BODY", chunk_size);
8560   SaveLevel_BODY(file, level);
8561
8562   chunk_size = SaveLevel_ELEM(NULL, level);
8563   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8564   {
8565     putFileChunkBE(file, "ELEM", chunk_size);
8566     SaveLevel_ELEM(file, level);
8567   }
8568
8569   for (i = 0; i < NUM_ENVELOPES; i++)
8570   {
8571     int element = EL_ENVELOPE_1 + i;
8572
8573     chunk_size = SaveLevel_NOTE(NULL, level, element);
8574     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8575     {
8576       putFileChunkBE(file, "NOTE", chunk_size);
8577       SaveLevel_NOTE(file, level, element);
8578     }
8579   }
8580
8581   // if not using template level, check for non-default custom/group elements
8582   if (!level->use_custom_template || save_as_template)
8583   {
8584     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8585     {
8586       int element = EL_CUSTOM_START + i;
8587
8588       chunk_size = SaveLevel_CUSX(NULL, level, element);
8589       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8590       {
8591         putFileChunkBE(file, "CUSX", chunk_size);
8592         SaveLevel_CUSX(file, level, element);
8593       }
8594     }
8595
8596     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8597     {
8598       int element = EL_GROUP_START + i;
8599
8600       chunk_size = SaveLevel_GRPX(NULL, level, element);
8601       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8602       {
8603         putFileChunkBE(file, "GRPX", chunk_size);
8604         SaveLevel_GRPX(file, level, element);
8605       }
8606     }
8607
8608     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8609     {
8610       int element = GET_EMPTY_ELEMENT(i);
8611
8612       chunk_size = SaveLevel_EMPX(NULL, level, element);
8613       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8614       {
8615         putFileChunkBE(file, "EMPX", chunk_size);
8616         SaveLevel_EMPX(file, level, element);
8617       }
8618     }
8619   }
8620
8621   fclose(file);
8622
8623   SetFilePermissions(filename, PERMS_PRIVATE);
8624 }
8625
8626 void SaveLevel(int nr)
8627 {
8628   char *filename = getDefaultLevelFilename(nr);
8629
8630   SaveLevelFromFilename(&level, filename, FALSE);
8631 }
8632
8633 void SaveLevelTemplate(void)
8634 {
8635   char *filename = getLocalLevelTemplateFilename();
8636
8637   SaveLevelFromFilename(&level, filename, TRUE);
8638 }
8639
8640 boolean SaveLevelChecked(int nr)
8641 {
8642   char *filename = getDefaultLevelFilename(nr);
8643   boolean new_level = !fileExists(filename);
8644   boolean level_saved = FALSE;
8645
8646   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8647   {
8648     SaveLevel(nr);
8649
8650     if (new_level)
8651       Request("Level saved!", REQ_CONFIRM);
8652
8653     level_saved = TRUE;
8654   }
8655
8656   return level_saved;
8657 }
8658
8659 void DumpLevel(struct LevelInfo *level)
8660 {
8661   if (level->no_level_file || level->no_valid_file)
8662   {
8663     Warn("cannot dump -- no valid level file found");
8664
8665     return;
8666   }
8667
8668   PrintLine("-", 79);
8669   Print("Level xxx (file version %08d, game version %08d)\n",
8670         level->file_version, level->game_version);
8671   PrintLine("-", 79);
8672
8673   Print("Level author: '%s'\n", level->author);
8674   Print("Level title:  '%s'\n", level->name);
8675   Print("\n");
8676   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8677   Print("\n");
8678   Print("Level time:  %d seconds\n", level->time);
8679   Print("Gems needed: %d\n", level->gems_needed);
8680   Print("\n");
8681   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8682   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8683   Print("Time for light:      %d seconds\n", level->time_light);
8684   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8685   Print("\n");
8686   Print("Amoeba speed: %d\n", level->amoeba_speed);
8687   Print("\n");
8688
8689   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8690   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8691   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8692   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8693   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8694   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8695
8696   if (options.debug)
8697   {
8698     int i, j;
8699
8700     for (i = 0; i < NUM_ENVELOPES; i++)
8701     {
8702       char *text = level->envelope[i].text;
8703       int text_len = strlen(text);
8704       boolean has_text = FALSE;
8705
8706       for (j = 0; j < text_len; j++)
8707         if (text[j] != ' ' && text[j] != '\n')
8708           has_text = TRUE;
8709
8710       if (has_text)
8711       {
8712         Print("\n");
8713         Print("Envelope %d:\n'%s'\n", i + 1, text);
8714       }
8715     }
8716   }
8717
8718   PrintLine("-", 79);
8719 }
8720
8721 void DumpLevels(void)
8722 {
8723   static LevelDirTree *dumplevel_leveldir = NULL;
8724
8725   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8726                                                  global.dumplevel_leveldir);
8727
8728   if (dumplevel_leveldir == NULL)
8729     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8730
8731   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8732       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8733     Fail("no such level number: %d", global.dumplevel_level_nr);
8734
8735   leveldir_current = dumplevel_leveldir;
8736
8737   LoadLevel(global.dumplevel_level_nr);
8738   DumpLevel(&level);
8739
8740   CloseAllAndExit(0);
8741 }
8742
8743
8744 // ============================================================================
8745 // tape file functions
8746 // ============================================================================
8747
8748 static void setTapeInfoToDefaults(void)
8749 {
8750   int i;
8751
8752   // always start with reliable default values (empty tape)
8753   TapeErase();
8754
8755   // default values (also for pre-1.2 tapes) with only the first player
8756   tape.player_participates[0] = TRUE;
8757   for (i = 1; i < MAX_PLAYERS; i++)
8758     tape.player_participates[i] = FALSE;
8759
8760   // at least one (default: the first) player participates in every tape
8761   tape.num_participating_players = 1;
8762
8763   tape.property_bits = TAPE_PROPERTY_NONE;
8764
8765   tape.level_nr = level_nr;
8766   tape.counter = 0;
8767   tape.changed = FALSE;
8768   tape.solved = FALSE;
8769
8770   tape.recording = FALSE;
8771   tape.playing = FALSE;
8772   tape.pausing = FALSE;
8773
8774   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8775   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8776
8777   tape.no_info_chunk = TRUE;
8778   tape.no_valid_file = FALSE;
8779 }
8780
8781 static int getTapePosSize(struct TapeInfo *tape)
8782 {
8783   int tape_pos_size = 0;
8784
8785   if (tape->use_key_actions)
8786     tape_pos_size += tape->num_participating_players;
8787
8788   if (tape->use_mouse_actions)
8789     tape_pos_size += 3;         // x and y position and mouse button mask
8790
8791   tape_pos_size += 1;           // tape action delay value
8792
8793   return tape_pos_size;
8794 }
8795
8796 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8797 {
8798   tape->use_key_actions = FALSE;
8799   tape->use_mouse_actions = FALSE;
8800
8801   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8802     tape->use_key_actions = TRUE;
8803
8804   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8805     tape->use_mouse_actions = TRUE;
8806 }
8807
8808 static int getTapeActionValue(struct TapeInfo *tape)
8809 {
8810   return (tape->use_key_actions &&
8811           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8812           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8813           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8814           TAPE_ACTIONS_DEFAULT);
8815 }
8816
8817 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8818 {
8819   tape->file_version = getFileVersion(file);
8820   tape->game_version = getFileVersion(file);
8821
8822   return chunk_size;
8823 }
8824
8825 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8826 {
8827   int i;
8828
8829   tape->random_seed = getFile32BitBE(file);
8830   tape->date        = getFile32BitBE(file);
8831   tape->length      = getFile32BitBE(file);
8832
8833   // read header fields that are new since version 1.2
8834   if (tape->file_version >= FILE_VERSION_1_2)
8835   {
8836     byte store_participating_players = getFile8Bit(file);
8837     int engine_version;
8838
8839     // since version 1.2, tapes store which players participate in the tape
8840     tape->num_participating_players = 0;
8841     for (i = 0; i < MAX_PLAYERS; i++)
8842     {
8843       tape->player_participates[i] = FALSE;
8844
8845       if (store_participating_players & (1 << i))
8846       {
8847         tape->player_participates[i] = TRUE;
8848         tape->num_participating_players++;
8849       }
8850     }
8851
8852     setTapeActionFlags(tape, getFile8Bit(file));
8853
8854     tape->property_bits = getFile8Bit(file);
8855     tape->solved = getFile8Bit(file);
8856
8857     engine_version = getFileVersion(file);
8858     if (engine_version > 0)
8859       tape->engine_version = engine_version;
8860     else
8861       tape->engine_version = tape->game_version;
8862   }
8863
8864   return chunk_size;
8865 }
8866
8867 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8868 {
8869   tape->scr_fieldx = getFile8Bit(file);
8870   tape->scr_fieldy = getFile8Bit(file);
8871
8872   return chunk_size;
8873 }
8874
8875 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8876 {
8877   char *level_identifier = NULL;
8878   int level_identifier_size;
8879   int i;
8880
8881   tape->no_info_chunk = FALSE;
8882
8883   level_identifier_size = getFile16BitBE(file);
8884
8885   level_identifier = checked_malloc(level_identifier_size);
8886
8887   for (i = 0; i < level_identifier_size; i++)
8888     level_identifier[i] = getFile8Bit(file);
8889
8890   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8891   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8892
8893   checked_free(level_identifier);
8894
8895   tape->level_nr = getFile16BitBE(file);
8896
8897   chunk_size = 2 + level_identifier_size + 2;
8898
8899   return chunk_size;
8900 }
8901
8902 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8903 {
8904   int i, j;
8905   int tape_pos_size = getTapePosSize(tape);
8906   int chunk_size_expected = tape_pos_size * tape->length;
8907
8908   if (chunk_size_expected != chunk_size)
8909   {
8910     ReadUnusedBytesFromFile(file, chunk_size);
8911     return chunk_size_expected;
8912   }
8913
8914   for (i = 0; i < tape->length; i++)
8915   {
8916     if (i >= MAX_TAPE_LEN)
8917     {
8918       Warn("tape truncated -- size exceeds maximum tape size %d",
8919             MAX_TAPE_LEN);
8920
8921       // tape too large; read and ignore remaining tape data from this chunk
8922       for (;i < tape->length; i++)
8923         ReadUnusedBytesFromFile(file, tape_pos_size);
8924
8925       break;
8926     }
8927
8928     if (tape->use_key_actions)
8929     {
8930       for (j = 0; j < MAX_PLAYERS; j++)
8931       {
8932         tape->pos[i].action[j] = MV_NONE;
8933
8934         if (tape->player_participates[j])
8935           tape->pos[i].action[j] = getFile8Bit(file);
8936       }
8937     }
8938
8939     if (tape->use_mouse_actions)
8940     {
8941       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8942       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8943       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8944     }
8945
8946     tape->pos[i].delay = getFile8Bit(file);
8947
8948     if (tape->file_version == FILE_VERSION_1_0)
8949     {
8950       // eliminate possible diagonal moves in old tapes
8951       // this is only for backward compatibility
8952
8953       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8954       byte action = tape->pos[i].action[0];
8955       int k, num_moves = 0;
8956
8957       for (k = 0; k < 4; k++)
8958       {
8959         if (action & joy_dir[k])
8960         {
8961           tape->pos[i + num_moves].action[0] = joy_dir[k];
8962           if (num_moves > 0)
8963             tape->pos[i + num_moves].delay = 0;
8964           num_moves++;
8965         }
8966       }
8967
8968       if (num_moves > 1)
8969       {
8970         num_moves--;
8971         i += num_moves;
8972         tape->length += num_moves;
8973       }
8974     }
8975     else if (tape->file_version < FILE_VERSION_2_0)
8976     {
8977       // convert pre-2.0 tapes to new tape format
8978
8979       if (tape->pos[i].delay > 1)
8980       {
8981         // action part
8982         tape->pos[i + 1] = tape->pos[i];
8983         tape->pos[i + 1].delay = 1;
8984
8985         // delay part
8986         for (j = 0; j < MAX_PLAYERS; j++)
8987           tape->pos[i].action[j] = MV_NONE;
8988         tape->pos[i].delay--;
8989
8990         i++;
8991         tape->length++;
8992       }
8993     }
8994
8995     if (checkEndOfFile(file))
8996       break;
8997   }
8998
8999   if (i != tape->length)
9000     chunk_size = tape_pos_size * i;
9001
9002   return chunk_size;
9003 }
9004
9005 static void LoadTape_SokobanSolution(char *filename)
9006 {
9007   File *file;
9008   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9009
9010   if (!(file = openFile(filename, MODE_READ)))
9011   {
9012     tape.no_valid_file = TRUE;
9013
9014     return;
9015   }
9016
9017   while (!checkEndOfFile(file))
9018   {
9019     unsigned char c = getByteFromFile(file);
9020
9021     if (checkEndOfFile(file))
9022       break;
9023
9024     switch (c)
9025     {
9026       case 'u':
9027       case 'U':
9028         tape.pos[tape.length].action[0] = MV_UP;
9029         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9030         tape.length++;
9031         break;
9032
9033       case 'd':
9034       case 'D':
9035         tape.pos[tape.length].action[0] = MV_DOWN;
9036         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9037         tape.length++;
9038         break;
9039
9040       case 'l':
9041       case 'L':
9042         tape.pos[tape.length].action[0] = MV_LEFT;
9043         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9044         tape.length++;
9045         break;
9046
9047       case 'r':
9048       case 'R':
9049         tape.pos[tape.length].action[0] = MV_RIGHT;
9050         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9051         tape.length++;
9052         break;
9053
9054       case '\n':
9055       case '\r':
9056       case '\t':
9057       case ' ':
9058         // ignore white-space characters
9059         break;
9060
9061       default:
9062         tape.no_valid_file = TRUE;
9063
9064         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9065
9066         break;
9067     }
9068   }
9069
9070   closeFile(file);
9071
9072   if (tape.no_valid_file)
9073     return;
9074
9075   tape.length_frames  = GetTapeLengthFrames();
9076   tape.length_seconds = GetTapeLengthSeconds();
9077 }
9078
9079 void LoadTapeFromFilename(char *filename)
9080 {
9081   char cookie[MAX_LINE_LEN];
9082   char chunk_name[CHUNK_ID_LEN + 1];
9083   File *file;
9084   int chunk_size;
9085
9086   // always start with reliable default values
9087   setTapeInfoToDefaults();
9088
9089   if (strSuffix(filename, ".sln"))
9090   {
9091     LoadTape_SokobanSolution(filename);
9092
9093     return;
9094   }
9095
9096   if (!(file = openFile(filename, MODE_READ)))
9097   {
9098     tape.no_valid_file = TRUE;
9099
9100     return;
9101   }
9102
9103   getFileChunkBE(file, chunk_name, NULL);
9104   if (strEqual(chunk_name, "RND1"))
9105   {
9106     getFile32BitBE(file);               // not used
9107
9108     getFileChunkBE(file, chunk_name, NULL);
9109     if (!strEqual(chunk_name, "TAPE"))
9110     {
9111       tape.no_valid_file = TRUE;
9112
9113       Warn("unknown format of tape file '%s'", filename);
9114
9115       closeFile(file);
9116
9117       return;
9118     }
9119   }
9120   else  // check for pre-2.0 file format with cookie string
9121   {
9122     strcpy(cookie, chunk_name);
9123     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9124       cookie[4] = '\0';
9125     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9126       cookie[strlen(cookie) - 1] = '\0';
9127
9128     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9129     {
9130       tape.no_valid_file = TRUE;
9131
9132       Warn("unknown format of tape file '%s'", filename);
9133
9134       closeFile(file);
9135
9136       return;
9137     }
9138
9139     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9140     {
9141       tape.no_valid_file = TRUE;
9142
9143       Warn("unsupported version of tape file '%s'", filename);
9144
9145       closeFile(file);
9146
9147       return;
9148     }
9149
9150     // pre-2.0 tape files have no game version, so use file version here
9151     tape.game_version = tape.file_version;
9152   }
9153
9154   if (tape.file_version < FILE_VERSION_1_2)
9155   {
9156     // tape files from versions before 1.2.0 without chunk structure
9157     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9158     LoadTape_BODY(file, 2 * tape.length,      &tape);
9159   }
9160   else
9161   {
9162     static struct
9163     {
9164       char *name;
9165       int size;
9166       int (*loader)(File *, int, struct TapeInfo *);
9167     }
9168     chunk_info[] =
9169     {
9170       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9171       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9172       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9173       { "INFO", -1,                     LoadTape_INFO },
9174       { "BODY", -1,                     LoadTape_BODY },
9175       {  NULL,  0,                      NULL }
9176     };
9177
9178     while (getFileChunkBE(file, chunk_name, &chunk_size))
9179     {
9180       int i = 0;
9181
9182       while (chunk_info[i].name != NULL &&
9183              !strEqual(chunk_name, chunk_info[i].name))
9184         i++;
9185
9186       if (chunk_info[i].name == NULL)
9187       {
9188         Warn("unknown chunk '%s' in tape file '%s'",
9189               chunk_name, filename);
9190
9191         ReadUnusedBytesFromFile(file, chunk_size);
9192       }
9193       else if (chunk_info[i].size != -1 &&
9194                chunk_info[i].size != chunk_size)
9195       {
9196         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9197               chunk_size, chunk_name, filename);
9198
9199         ReadUnusedBytesFromFile(file, chunk_size);
9200       }
9201       else
9202       {
9203         // call function to load this tape chunk
9204         int chunk_size_expected =
9205           (chunk_info[i].loader)(file, chunk_size, &tape);
9206
9207         // the size of some chunks cannot be checked before reading other
9208         // chunks first (like "HEAD" and "BODY") that contain some header
9209         // information, so check them here
9210         if (chunk_size_expected != chunk_size)
9211         {
9212           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9213                 chunk_size, chunk_name, filename);
9214         }
9215       }
9216     }
9217   }
9218
9219   closeFile(file);
9220
9221   tape.length_frames  = GetTapeLengthFrames();
9222   tape.length_seconds = GetTapeLengthSeconds();
9223
9224 #if 0
9225   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9226         tape.file_version);
9227   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9228         tape.game_version);
9229   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9230         tape.engine_version);
9231 #endif
9232 }
9233
9234 void LoadTape(int nr)
9235 {
9236   char *filename = getTapeFilename(nr);
9237
9238   LoadTapeFromFilename(filename);
9239 }
9240
9241 void LoadSolutionTape(int nr)
9242 {
9243   char *filename = getSolutionTapeFilename(nr);
9244
9245   LoadTapeFromFilename(filename);
9246
9247   if (TAPE_IS_EMPTY(tape))
9248   {
9249     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9250         level.native_bd_level->replay != NULL)
9251       CopyNativeTape_BD_to_RND(&level);
9252     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9253         level.native_sp_level->demo.is_available)
9254       CopyNativeTape_SP_to_RND(&level);
9255   }
9256 }
9257
9258 void LoadScoreTape(char *score_tape_basename, int nr)
9259 {
9260   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9261
9262   LoadTapeFromFilename(filename);
9263 }
9264
9265 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9266 {
9267   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9268
9269   LoadTapeFromFilename(filename);
9270 }
9271
9272 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9273 {
9274   // chunk required for team mode tapes with non-default screen size
9275   return (tape->num_participating_players > 1 &&
9276           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9277            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9278 }
9279
9280 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9281 {
9282   putFileVersion(file, tape->file_version);
9283   putFileVersion(file, tape->game_version);
9284 }
9285
9286 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9287 {
9288   int i;
9289   byte store_participating_players = 0;
9290
9291   // set bits for participating players for compact storage
9292   for (i = 0; i < MAX_PLAYERS; i++)
9293     if (tape->player_participates[i])
9294       store_participating_players |= (1 << i);
9295
9296   putFile32BitBE(file, tape->random_seed);
9297   putFile32BitBE(file, tape->date);
9298   putFile32BitBE(file, tape->length);
9299
9300   putFile8Bit(file, store_participating_players);
9301
9302   putFile8Bit(file, getTapeActionValue(tape));
9303
9304   putFile8Bit(file, tape->property_bits);
9305   putFile8Bit(file, tape->solved);
9306
9307   putFileVersion(file, tape->engine_version);
9308 }
9309
9310 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9311 {
9312   putFile8Bit(file, tape->scr_fieldx);
9313   putFile8Bit(file, tape->scr_fieldy);
9314 }
9315
9316 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9317 {
9318   int level_identifier_size = strlen(tape->level_identifier) + 1;
9319   int i;
9320
9321   putFile16BitBE(file, level_identifier_size);
9322
9323   for (i = 0; i < level_identifier_size; i++)
9324     putFile8Bit(file, tape->level_identifier[i]);
9325
9326   putFile16BitBE(file, tape->level_nr);
9327 }
9328
9329 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9330 {
9331   int i, j;
9332
9333   for (i = 0; i < tape->length; i++)
9334   {
9335     if (tape->use_key_actions)
9336     {
9337       for (j = 0; j < MAX_PLAYERS; j++)
9338         if (tape->player_participates[j])
9339           putFile8Bit(file, tape->pos[i].action[j]);
9340     }
9341
9342     if (tape->use_mouse_actions)
9343     {
9344       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9345       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9346       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9347     }
9348
9349     putFile8Bit(file, tape->pos[i].delay);
9350   }
9351 }
9352
9353 void SaveTapeToFilename(char *filename)
9354 {
9355   FILE *file;
9356   int tape_pos_size;
9357   int info_chunk_size;
9358   int body_chunk_size;
9359
9360   if (!(file = fopen(filename, MODE_WRITE)))
9361   {
9362     Warn("cannot save level recording file '%s'", filename);
9363
9364     return;
9365   }
9366
9367   tape_pos_size = getTapePosSize(&tape);
9368
9369   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9370   body_chunk_size = tape_pos_size * tape.length;
9371
9372   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9373   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9374
9375   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9376   SaveTape_VERS(file, &tape);
9377
9378   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9379   SaveTape_HEAD(file, &tape);
9380
9381   if (checkSaveTape_SCRN(&tape))
9382   {
9383     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9384     SaveTape_SCRN(file, &tape);
9385   }
9386
9387   putFileChunkBE(file, "INFO", info_chunk_size);
9388   SaveTape_INFO(file, &tape);
9389
9390   putFileChunkBE(file, "BODY", body_chunk_size);
9391   SaveTape_BODY(file, &tape);
9392
9393   fclose(file);
9394
9395   SetFilePermissions(filename, PERMS_PRIVATE);
9396 }
9397
9398 static void SaveTapeExt(char *filename)
9399 {
9400   int i;
9401
9402   tape.file_version = FILE_VERSION_ACTUAL;
9403   tape.game_version = GAME_VERSION_ACTUAL;
9404
9405   tape.num_participating_players = 0;
9406
9407   // count number of participating players
9408   for (i = 0; i < MAX_PLAYERS; i++)
9409     if (tape.player_participates[i])
9410       tape.num_participating_players++;
9411
9412   SaveTapeToFilename(filename);
9413
9414   tape.changed = FALSE;
9415 }
9416
9417 void SaveTape(int nr)
9418 {
9419   char *filename = getTapeFilename(nr);
9420
9421   InitTapeDirectory(leveldir_current->subdir);
9422
9423   SaveTapeExt(filename);
9424 }
9425
9426 void SaveScoreTape(int nr)
9427 {
9428   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9429
9430   // used instead of "leveldir_current->subdir" (for network games)
9431   InitScoreTapeDirectory(levelset.identifier, nr);
9432
9433   SaveTapeExt(filename);
9434 }
9435
9436 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9437                                   unsigned int req_state_added)
9438 {
9439   char *filename = getTapeFilename(nr);
9440   boolean new_tape = !fileExists(filename);
9441   boolean tape_saved = FALSE;
9442
9443   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9444   {
9445     SaveTape(nr);
9446
9447     if (new_tape)
9448       Request(msg_saved, REQ_CONFIRM | req_state_added);
9449
9450     tape_saved = TRUE;
9451   }
9452
9453   return tape_saved;
9454 }
9455
9456 boolean SaveTapeChecked(int nr)
9457 {
9458   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9459 }
9460
9461 boolean SaveTapeChecked_LevelSolved(int nr)
9462 {
9463   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9464                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9465 }
9466
9467 void DumpTape(struct TapeInfo *tape)
9468 {
9469   int tape_frame_counter;
9470   int i, j;
9471
9472   if (tape->no_valid_file)
9473   {
9474     Warn("cannot dump -- no valid tape file found");
9475
9476     return;
9477   }
9478
9479   PrintLine("-", 79);
9480
9481   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9482         tape->level_nr, tape->file_version, tape->game_version);
9483   Print("                  (effective engine version %08d)\n",
9484         tape->engine_version);
9485   Print("Level series identifier: '%s'\n", tape->level_identifier);
9486
9487   Print("Solution tape: %s\n",
9488         tape->solved ? "yes" :
9489         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9490
9491   Print("Special tape properties: ");
9492   if (tape->property_bits == TAPE_PROPERTY_NONE)
9493     Print("[none]");
9494   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9495     Print("[em_random_bug]");
9496   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9497     Print("[game_speed]");
9498   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9499     Print("[pause]");
9500   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9501     Print("[single_step]");
9502   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9503     Print("[snapshot]");
9504   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9505     Print("[replayed]");
9506   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9507     Print("[tas_keys]");
9508   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9509     Print("[small_graphics]");
9510   Print("\n");
9511
9512   int year2 = tape->date / 10000;
9513   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9514   int month_index_raw = (tape->date / 100) % 100;
9515   int month_index = month_index_raw % 12;       // prevent invalid index
9516   int month = month_index + 1;
9517   int day = tape->date % 100;
9518
9519   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9520
9521   PrintLine("-", 79);
9522
9523   tape_frame_counter = 0;
9524
9525   for (i = 0; i < tape->length; i++)
9526   {
9527     if (i >= MAX_TAPE_LEN)
9528       break;
9529
9530     Print("%04d: ", i);
9531
9532     for (j = 0; j < MAX_PLAYERS; j++)
9533     {
9534       if (tape->player_participates[j])
9535       {
9536         int action = tape->pos[i].action[j];
9537
9538         Print("%d:%02x ", j, action);
9539         Print("[%c%c%c%c|%c%c] - ",
9540               (action & JOY_LEFT ? '<' : ' '),
9541               (action & JOY_RIGHT ? '>' : ' '),
9542               (action & JOY_UP ? '^' : ' '),
9543               (action & JOY_DOWN ? 'v' : ' '),
9544               (action & JOY_BUTTON_1 ? '1' : ' '),
9545               (action & JOY_BUTTON_2 ? '2' : ' '));
9546       }
9547     }
9548
9549     Print("(%03d) ", tape->pos[i].delay);
9550     Print("[%05d]\n", tape_frame_counter);
9551
9552     tape_frame_counter += tape->pos[i].delay;
9553   }
9554
9555   PrintLine("-", 79);
9556 }
9557
9558 void DumpTapes(void)
9559 {
9560   static LevelDirTree *dumptape_leveldir = NULL;
9561
9562   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9563                                                 global.dumptape_leveldir);
9564
9565   if (dumptape_leveldir == NULL)
9566     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9567
9568   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9569       global.dumptape_level_nr > dumptape_leveldir->last_level)
9570     Fail("no such level number: %d", global.dumptape_level_nr);
9571
9572   leveldir_current = dumptape_leveldir;
9573
9574   if (options.mytapes)
9575     LoadTape(global.dumptape_level_nr);
9576   else
9577     LoadSolutionTape(global.dumptape_level_nr);
9578
9579   DumpTape(&tape);
9580
9581   CloseAllAndExit(0);
9582 }
9583
9584
9585 // ============================================================================
9586 // score file functions
9587 // ============================================================================
9588
9589 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9590 {
9591   int i;
9592
9593   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9594   {
9595     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9596     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9597     scores->entry[i].score = 0;
9598     scores->entry[i].time = 0;
9599
9600     scores->entry[i].id = -1;
9601     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9602     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9603     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9604     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9605     strcpy(scores->entry[i].country_code, "??");
9606   }
9607
9608   scores->num_entries = 0;
9609   scores->last_added = -1;
9610   scores->last_added_local = -1;
9611
9612   scores->updated = FALSE;
9613   scores->uploaded = FALSE;
9614   scores->tape_downloaded = FALSE;
9615   scores->force_last_added = FALSE;
9616
9617   // The following values are intentionally not reset here:
9618   // - last_level_nr
9619   // - last_entry_nr
9620   // - next_level_nr
9621   // - continue_playing
9622   // - continue_on_return
9623 }
9624
9625 static void setScoreInfoToDefaults(void)
9626 {
9627   setScoreInfoToDefaultsExt(&scores);
9628 }
9629
9630 static void setServerScoreInfoToDefaults(void)
9631 {
9632   setScoreInfoToDefaultsExt(&server_scores);
9633 }
9634
9635 static void LoadScore_OLD(int nr)
9636 {
9637   int i;
9638   char *filename = getScoreFilename(nr);
9639   char cookie[MAX_LINE_LEN];
9640   char line[MAX_LINE_LEN];
9641   char *line_ptr;
9642   FILE *file;
9643
9644   if (!(file = fopen(filename, MODE_READ)))
9645     return;
9646
9647   // check file identifier
9648   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9649     cookie[0] = '\0';
9650   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9651     cookie[strlen(cookie) - 1] = '\0';
9652
9653   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9654   {
9655     Warn("unknown format of score file '%s'", filename);
9656
9657     fclose(file);
9658
9659     return;
9660   }
9661
9662   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9663   {
9664     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9665       Warn("fscanf() failed; %s", strerror(errno));
9666
9667     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9668       line[0] = '\0';
9669
9670     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9671       line[strlen(line) - 1] = '\0';
9672
9673     for (line_ptr = line; *line_ptr; line_ptr++)
9674     {
9675       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9676       {
9677         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9678         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9679         break;
9680       }
9681     }
9682   }
9683
9684   fclose(file);
9685 }
9686
9687 static void ConvertScore_OLD(void)
9688 {
9689   // only convert score to time for levels that rate playing time over score
9690   if (!level.rate_time_over_score)
9691     return;
9692
9693   // convert old score to playing time for score-less levels (like Supaplex)
9694   int time_final_max = 999;
9695   int i;
9696
9697   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9698   {
9699     int score = scores.entry[i].score;
9700
9701     if (score > 0 && score < time_final_max)
9702       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9703   }
9704 }
9705
9706 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9707 {
9708   scores->file_version = getFileVersion(file);
9709   scores->game_version = getFileVersion(file);
9710
9711   return chunk_size;
9712 }
9713
9714 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9715 {
9716   char *level_identifier = NULL;
9717   int level_identifier_size;
9718   int i;
9719
9720   level_identifier_size = getFile16BitBE(file);
9721
9722   level_identifier = checked_malloc(level_identifier_size);
9723
9724   for (i = 0; i < level_identifier_size; i++)
9725     level_identifier[i] = getFile8Bit(file);
9726
9727   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9728   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9729
9730   checked_free(level_identifier);
9731
9732   scores->level_nr = getFile16BitBE(file);
9733   scores->num_entries = getFile16BitBE(file);
9734
9735   chunk_size = 2 + level_identifier_size + 2 + 2;
9736
9737   return chunk_size;
9738 }
9739
9740 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9741 {
9742   int i, j;
9743
9744   for (i = 0; i < scores->num_entries; i++)
9745   {
9746     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9747       scores->entry[i].name[j] = getFile8Bit(file);
9748
9749     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9750   }
9751
9752   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9753
9754   return chunk_size;
9755 }
9756
9757 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9758 {
9759   int i;
9760
9761   for (i = 0; i < scores->num_entries; i++)
9762     scores->entry[i].score = getFile16BitBE(file);
9763
9764   chunk_size = scores->num_entries * 2;
9765
9766   return chunk_size;
9767 }
9768
9769 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9770 {
9771   int i;
9772
9773   for (i = 0; i < scores->num_entries; i++)
9774     scores->entry[i].score = getFile32BitBE(file);
9775
9776   chunk_size = scores->num_entries * 4;
9777
9778   return chunk_size;
9779 }
9780
9781 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9782 {
9783   int i;
9784
9785   for (i = 0; i < scores->num_entries; i++)
9786     scores->entry[i].time = getFile32BitBE(file);
9787
9788   chunk_size = scores->num_entries * 4;
9789
9790   return chunk_size;
9791 }
9792
9793 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9794 {
9795   int i, j;
9796
9797   for (i = 0; i < scores->num_entries; i++)
9798   {
9799     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9800       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9801
9802     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9803   }
9804
9805   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9806
9807   return chunk_size;
9808 }
9809
9810 void LoadScore(int nr)
9811 {
9812   char *filename = getScoreFilename(nr);
9813   char cookie[MAX_LINE_LEN];
9814   char chunk_name[CHUNK_ID_LEN + 1];
9815   int chunk_size;
9816   boolean old_score_file_format = FALSE;
9817   File *file;
9818
9819   // always start with reliable default values
9820   setScoreInfoToDefaults();
9821
9822   if (!(file = openFile(filename, MODE_READ)))
9823     return;
9824
9825   getFileChunkBE(file, chunk_name, NULL);
9826   if (strEqual(chunk_name, "RND1"))
9827   {
9828     getFile32BitBE(file);               // not used
9829
9830     getFileChunkBE(file, chunk_name, NULL);
9831     if (!strEqual(chunk_name, "SCOR"))
9832     {
9833       Warn("unknown format of score file '%s'", filename);
9834
9835       closeFile(file);
9836
9837       return;
9838     }
9839   }
9840   else  // check for old file format with cookie string
9841   {
9842     strcpy(cookie, chunk_name);
9843     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9844       cookie[4] = '\0';
9845     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9846       cookie[strlen(cookie) - 1] = '\0';
9847
9848     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9849     {
9850       Warn("unknown format of score file '%s'", filename);
9851
9852       closeFile(file);
9853
9854       return;
9855     }
9856
9857     old_score_file_format = TRUE;
9858   }
9859
9860   if (old_score_file_format)
9861   {
9862     // score files from versions before 4.2.4.0 without chunk structure
9863     LoadScore_OLD(nr);
9864
9865     // convert score to time, if possible (mainly for Supaplex levels)
9866     ConvertScore_OLD();
9867   }
9868   else
9869   {
9870     static struct
9871     {
9872       char *name;
9873       int size;
9874       int (*loader)(File *, int, struct ScoreInfo *);
9875     }
9876     chunk_info[] =
9877     {
9878       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9879       { "INFO", -1,                     LoadScore_INFO },
9880       { "NAME", -1,                     LoadScore_NAME },
9881       { "SCOR", -1,                     LoadScore_SCOR },
9882       { "SC4R", -1,                     LoadScore_SC4R },
9883       { "TIME", -1,                     LoadScore_TIME },
9884       { "TAPE", -1,                     LoadScore_TAPE },
9885
9886       {  NULL,  0,                      NULL }
9887     };
9888
9889     while (getFileChunkBE(file, chunk_name, &chunk_size))
9890     {
9891       int i = 0;
9892
9893       while (chunk_info[i].name != NULL &&
9894              !strEqual(chunk_name, chunk_info[i].name))
9895         i++;
9896
9897       if (chunk_info[i].name == NULL)
9898       {
9899         Warn("unknown chunk '%s' in score file '%s'",
9900               chunk_name, filename);
9901
9902         ReadUnusedBytesFromFile(file, chunk_size);
9903       }
9904       else if (chunk_info[i].size != -1 &&
9905                chunk_info[i].size != chunk_size)
9906       {
9907         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9908               chunk_size, chunk_name, filename);
9909
9910         ReadUnusedBytesFromFile(file, chunk_size);
9911       }
9912       else
9913       {
9914         // call function to load this score chunk
9915         int chunk_size_expected =
9916           (chunk_info[i].loader)(file, chunk_size, &scores);
9917
9918         // the size of some chunks cannot be checked before reading other
9919         // chunks first (like "HEAD" and "BODY") that contain some header
9920         // information, so check them here
9921         if (chunk_size_expected != chunk_size)
9922         {
9923           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9924                 chunk_size, chunk_name, filename);
9925         }
9926       }
9927     }
9928   }
9929
9930   closeFile(file);
9931 }
9932
9933 #if ENABLE_HISTORIC_CHUNKS
9934 void SaveScore_OLD(int nr)
9935 {
9936   int i;
9937   char *filename = getScoreFilename(nr);
9938   FILE *file;
9939
9940   // used instead of "leveldir_current->subdir" (for network games)
9941   InitScoreDirectory(levelset.identifier);
9942
9943   if (!(file = fopen(filename, MODE_WRITE)))
9944   {
9945     Warn("cannot save score for level %d", nr);
9946
9947     return;
9948   }
9949
9950   fprintf(file, "%s\n\n", SCORE_COOKIE);
9951
9952   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9953     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9954
9955   fclose(file);
9956
9957   SetFilePermissions(filename, PERMS_PRIVATE);
9958 }
9959 #endif
9960
9961 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9962 {
9963   putFileVersion(file, scores->file_version);
9964   putFileVersion(file, scores->game_version);
9965 }
9966
9967 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9968 {
9969   int level_identifier_size = strlen(scores->level_identifier) + 1;
9970   int i;
9971
9972   putFile16BitBE(file, level_identifier_size);
9973
9974   for (i = 0; i < level_identifier_size; i++)
9975     putFile8Bit(file, scores->level_identifier[i]);
9976
9977   putFile16BitBE(file, scores->level_nr);
9978   putFile16BitBE(file, scores->num_entries);
9979 }
9980
9981 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9982 {
9983   int i, j;
9984
9985   for (i = 0; i < scores->num_entries; i++)
9986   {
9987     int name_size = strlen(scores->entry[i].name);
9988
9989     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9990       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9991   }
9992 }
9993
9994 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9995 {
9996   int i;
9997
9998   for (i = 0; i < scores->num_entries; i++)
9999     putFile16BitBE(file, scores->entry[i].score);
10000 }
10001
10002 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10003 {
10004   int i;
10005
10006   for (i = 0; i < scores->num_entries; i++)
10007     putFile32BitBE(file, scores->entry[i].score);
10008 }
10009
10010 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10011 {
10012   int i;
10013
10014   for (i = 0; i < scores->num_entries; i++)
10015     putFile32BitBE(file, scores->entry[i].time);
10016 }
10017
10018 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10019 {
10020   int i, j;
10021
10022   for (i = 0; i < scores->num_entries; i++)
10023   {
10024     int size = strlen(scores->entry[i].tape_basename);
10025
10026     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10027       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10028   }
10029 }
10030
10031 static void SaveScoreToFilename(char *filename)
10032 {
10033   FILE *file;
10034   int info_chunk_size;
10035   int name_chunk_size;
10036   int scor_chunk_size;
10037   int sc4r_chunk_size;
10038   int time_chunk_size;
10039   int tape_chunk_size;
10040   boolean has_large_score_values;
10041   int i;
10042
10043   if (!(file = fopen(filename, MODE_WRITE)))
10044   {
10045     Warn("cannot save score file '%s'", filename);
10046
10047     return;
10048   }
10049
10050   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10051   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10052   scor_chunk_size = scores.num_entries * 2;
10053   sc4r_chunk_size = scores.num_entries * 4;
10054   time_chunk_size = scores.num_entries * 4;
10055   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10056
10057   has_large_score_values = FALSE;
10058   for (i = 0; i < scores.num_entries; i++)
10059     if (scores.entry[i].score > 0xffff)
10060       has_large_score_values = TRUE;
10061
10062   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10063   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10064
10065   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10066   SaveScore_VERS(file, &scores);
10067
10068   putFileChunkBE(file, "INFO", info_chunk_size);
10069   SaveScore_INFO(file, &scores);
10070
10071   putFileChunkBE(file, "NAME", name_chunk_size);
10072   SaveScore_NAME(file, &scores);
10073
10074   if (has_large_score_values)
10075   {
10076     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10077     SaveScore_SC4R(file, &scores);
10078   }
10079   else
10080   {
10081     putFileChunkBE(file, "SCOR", scor_chunk_size);
10082     SaveScore_SCOR(file, &scores);
10083   }
10084
10085   putFileChunkBE(file, "TIME", time_chunk_size);
10086   SaveScore_TIME(file, &scores);
10087
10088   putFileChunkBE(file, "TAPE", tape_chunk_size);
10089   SaveScore_TAPE(file, &scores);
10090
10091   fclose(file);
10092
10093   SetFilePermissions(filename, PERMS_PRIVATE);
10094 }
10095
10096 void SaveScore(int nr)
10097 {
10098   char *filename = getScoreFilename(nr);
10099   int i;
10100
10101   // used instead of "leveldir_current->subdir" (for network games)
10102   InitScoreDirectory(levelset.identifier);
10103
10104   scores.file_version = FILE_VERSION_ACTUAL;
10105   scores.game_version = GAME_VERSION_ACTUAL;
10106
10107   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10108   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10109   scores.level_nr = level_nr;
10110
10111   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10112     if (scores.entry[i].score == 0 &&
10113         scores.entry[i].time == 0 &&
10114         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10115       break;
10116
10117   scores.num_entries = i;
10118
10119   if (scores.num_entries == 0)
10120     return;
10121
10122   SaveScoreToFilename(filename);
10123 }
10124
10125 static void LoadServerScoreFromCache(int nr)
10126 {
10127   struct ScoreEntry score_entry;
10128   struct
10129   {
10130     void *value;
10131     boolean is_string;
10132     int string_size;
10133   }
10134   score_mapping[] =
10135   {
10136     { &score_entry.score,               FALSE,  0                       },
10137     { &score_entry.time,                FALSE,  0                       },
10138     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10139     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10140     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10141     { &score_entry.id,                  FALSE,  0                       },
10142     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10143     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10144     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10145     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10146
10147     { NULL,                             FALSE,  0                       }
10148   };
10149   char *filename = getScoreCacheFilename(nr);
10150   SetupFileHash *score_hash = loadSetupFileHash(filename);
10151   int i, j;
10152
10153   server_scores.num_entries = 0;
10154
10155   if (score_hash == NULL)
10156     return;
10157
10158   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10159   {
10160     score_entry = server_scores.entry[i];
10161
10162     for (j = 0; score_mapping[j].value != NULL; j++)
10163     {
10164       char token[10];
10165
10166       sprintf(token, "%02d.%d", i, j);
10167
10168       char *value = getHashEntry(score_hash, token);
10169
10170       if (value == NULL)
10171         continue;
10172
10173       if (score_mapping[j].is_string)
10174       {
10175         char *score_value = (char *)score_mapping[j].value;
10176         int value_size = score_mapping[j].string_size;
10177
10178         strncpy(score_value, value, value_size);
10179         score_value[value_size] = '\0';
10180       }
10181       else
10182       {
10183         int *score_value = (int *)score_mapping[j].value;
10184
10185         *score_value = atoi(value);
10186       }
10187
10188       server_scores.num_entries = i + 1;
10189     }
10190
10191     server_scores.entry[i] = score_entry;
10192   }
10193
10194   freeSetupFileHash(score_hash);
10195 }
10196
10197 void LoadServerScore(int nr, boolean download_score)
10198 {
10199   if (!setup.use_api_server)
10200     return;
10201
10202   // always start with reliable default values
10203   setServerScoreInfoToDefaults();
10204
10205   // 1st step: load server scores from cache file (which may not exist)
10206   // (this should prevent reading it while the thread is writing to it)
10207   LoadServerScoreFromCache(nr);
10208
10209   if (download_score && runtime.use_api_server)
10210   {
10211     // 2nd step: download server scores from score server to cache file
10212     // (as thread, as it might time out if the server is not reachable)
10213     ApiGetScoreAsThread(nr);
10214   }
10215 }
10216
10217 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10218 {
10219   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10220
10221   // if score tape not uploaded, ask for uploading missing tapes later
10222   if (!setup.has_remaining_tapes)
10223     setup.ask_for_remaining_tapes = TRUE;
10224
10225   setup.provide_uploading_tapes = TRUE;
10226   setup.has_remaining_tapes = TRUE;
10227
10228   SaveSetup_ServerSetup();
10229 }
10230
10231 void SaveServerScore(int nr, boolean tape_saved)
10232 {
10233   if (!runtime.use_api_server)
10234   {
10235     PrepareScoreTapesForUpload(leveldir_current->subdir);
10236
10237     return;
10238   }
10239
10240   ApiAddScoreAsThread(nr, tape_saved, NULL);
10241 }
10242
10243 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10244                              char *score_tape_filename)
10245 {
10246   if (!runtime.use_api_server)
10247     return;
10248
10249   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10250 }
10251
10252 void LoadLocalAndServerScore(int nr, boolean download_score)
10253 {
10254   int last_added_local = scores.last_added_local;
10255   boolean force_last_added = scores.force_last_added;
10256
10257   // needed if only showing server scores
10258   setScoreInfoToDefaults();
10259
10260   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10261     LoadScore(nr);
10262
10263   // restore last added local score entry (before merging server scores)
10264   scores.last_added = scores.last_added_local = last_added_local;
10265
10266   if (setup.use_api_server &&
10267       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10268   {
10269     // load server scores from cache file and trigger update from server
10270     LoadServerScore(nr, download_score);
10271
10272     // merge local scores with scores from server
10273     MergeServerScore();
10274   }
10275
10276   if (force_last_added)
10277     scores.force_last_added = force_last_added;
10278 }
10279
10280
10281 // ============================================================================
10282 // setup file functions
10283 // ============================================================================
10284
10285 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10286
10287
10288 static struct TokenInfo global_setup_tokens[] =
10289 {
10290   {
10291     TYPE_STRING,
10292     &setup.player_name,                         "player_name"
10293   },
10294   {
10295     TYPE_SWITCH,
10296     &setup.multiple_users,                      "multiple_users"
10297   },
10298   {
10299     TYPE_SWITCH,
10300     &setup.sound,                               "sound"
10301   },
10302   {
10303     TYPE_SWITCH,
10304     &setup.sound_loops,                         "repeating_sound_loops"
10305   },
10306   {
10307     TYPE_SWITCH,
10308     &setup.sound_music,                         "background_music"
10309   },
10310   {
10311     TYPE_SWITCH,
10312     &setup.sound_simple,                        "simple_sound_effects"
10313   },
10314   {
10315     TYPE_SWITCH,
10316     &setup.toons,                               "toons"
10317   },
10318   {
10319     TYPE_SWITCH,
10320     &setup.global_animations,                   "global_animations"
10321   },
10322   {
10323     TYPE_SWITCH,
10324     &setup.scroll_delay,                        "scroll_delay"
10325   },
10326   {
10327     TYPE_SWITCH,
10328     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10329   },
10330   {
10331     TYPE_INTEGER,
10332     &setup.scroll_delay_value,                  "scroll_delay_value"
10333   },
10334   {
10335     TYPE_STRING,
10336     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10337   },
10338   {
10339     TYPE_INTEGER,
10340     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10341   },
10342   {
10343     TYPE_SWITCH,
10344     &setup.fade_screens,                        "fade_screens"
10345   },
10346   {
10347     TYPE_SWITCH,
10348     &setup.autorecord,                          "automatic_tape_recording"
10349   },
10350   {
10351     TYPE_SWITCH,
10352     &setup.autorecord_after_replay,             "autorecord_after_replay"
10353   },
10354   {
10355     TYPE_SWITCH,
10356     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10357   },
10358   {
10359     TYPE_SWITCH,
10360     &setup.show_titlescreen,                    "show_titlescreen"
10361   },
10362   {
10363     TYPE_SWITCH,
10364     &setup.quick_doors,                         "quick_doors"
10365   },
10366   {
10367     TYPE_SWITCH,
10368     &setup.team_mode,                           "team_mode"
10369   },
10370   {
10371     TYPE_SWITCH,
10372     &setup.handicap,                            "handicap"
10373   },
10374   {
10375     TYPE_SWITCH,
10376     &setup.skip_levels,                         "skip_levels"
10377   },
10378   {
10379     TYPE_SWITCH,
10380     &setup.increment_levels,                    "increment_levels"
10381   },
10382   {
10383     TYPE_SWITCH,
10384     &setup.auto_play_next_level,                "auto_play_next_level"
10385   },
10386   {
10387     TYPE_SWITCH,
10388     &setup.count_score_after_game,              "count_score_after_game"
10389   },
10390   {
10391     TYPE_SWITCH,
10392     &setup.show_scores_after_game,              "show_scores_after_game"
10393   },
10394   {
10395     TYPE_SWITCH,
10396     &setup.time_limit,                          "time_limit"
10397   },
10398   {
10399     TYPE_SWITCH,
10400     &setup.fullscreen,                          "fullscreen"
10401   },
10402   {
10403     TYPE_INTEGER,
10404     &setup.window_scaling_percent,              "window_scaling_percent"
10405   },
10406   {
10407     TYPE_STRING,
10408     &setup.window_scaling_quality,              "window_scaling_quality"
10409   },
10410   {
10411     TYPE_STRING,
10412     &setup.screen_rendering_mode,               "screen_rendering_mode"
10413   },
10414   {
10415     TYPE_STRING,
10416     &setup.vsync_mode,                          "vsync_mode"
10417   },
10418   {
10419     TYPE_SWITCH,
10420     &setup.ask_on_escape,                       "ask_on_escape"
10421   },
10422   {
10423     TYPE_SWITCH,
10424     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10425   },
10426   {
10427     TYPE_SWITCH,
10428     &setup.ask_on_game_over,                    "ask_on_game_over"
10429   },
10430   {
10431     TYPE_SWITCH,
10432     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10433   },
10434   {
10435     TYPE_SWITCH,
10436     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10437   },
10438   {
10439     TYPE_SWITCH,
10440     &setup.quick_switch,                        "quick_player_switch"
10441   },
10442   {
10443     TYPE_SWITCH,
10444     &setup.input_on_focus,                      "input_on_focus"
10445   },
10446   {
10447     TYPE_SWITCH,
10448     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10449   },
10450   {
10451     TYPE_SWITCH,
10452     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10453   },
10454   {
10455     TYPE_SWITCH,
10456     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10457   },
10458   {
10459     TYPE_SWITCH,
10460     &setup.game_speed_extended,                 "game_speed_extended"
10461   },
10462   {
10463     TYPE_INTEGER,
10464     &setup.game_frame_delay,                    "game_frame_delay"
10465   },
10466   {
10467     TYPE_SWITCH,
10468     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10469   },
10470   {
10471     TYPE_SWITCH,
10472     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10473   },
10474   {
10475     TYPE_SWITCH,
10476     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10477   },
10478   {
10479     TYPE_SWITCH3,
10480     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10481   },
10482   {
10483     TYPE_SWITCH,
10484     &setup.sp_show_border_elements,             "sp_show_border_elements"
10485   },
10486   {
10487     TYPE_SWITCH,
10488     &setup.small_game_graphics,                 "small_game_graphics"
10489   },
10490   {
10491     TYPE_SWITCH,
10492     &setup.show_load_save_buttons,              "show_load_save_buttons"
10493   },
10494   {
10495     TYPE_SWITCH,
10496     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10497   },
10498   {
10499     TYPE_STRING,
10500     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10501   },
10502   {
10503     TYPE_STRING,
10504     &setup.graphics_set,                        "graphics_set"
10505   },
10506   {
10507     TYPE_STRING,
10508     &setup.sounds_set,                          "sounds_set"
10509   },
10510   {
10511     TYPE_STRING,
10512     &setup.music_set,                           "music_set"
10513   },
10514   {
10515     TYPE_SWITCH3,
10516     &setup.override_level_graphics,             "override_level_graphics"
10517   },
10518   {
10519     TYPE_SWITCH3,
10520     &setup.override_level_sounds,               "override_level_sounds"
10521   },
10522   {
10523     TYPE_SWITCH3,
10524     &setup.override_level_music,                "override_level_music"
10525   },
10526   {
10527     TYPE_INTEGER,
10528     &setup.volume_simple,                       "volume_simple"
10529   },
10530   {
10531     TYPE_INTEGER,
10532     &setup.volume_loops,                        "volume_loops"
10533   },
10534   {
10535     TYPE_INTEGER,
10536     &setup.volume_music,                        "volume_music"
10537   },
10538   {
10539     TYPE_SWITCH,
10540     &setup.network_mode,                        "network_mode"
10541   },
10542   {
10543     TYPE_PLAYER,
10544     &setup.network_player_nr,                   "network_player"
10545   },
10546   {
10547     TYPE_STRING,
10548     &setup.network_server_hostname,             "network_server_hostname"
10549   },
10550   {
10551     TYPE_STRING,
10552     &setup.touch.control_type,                  "touch.control_type"
10553   },
10554   {
10555     TYPE_INTEGER,
10556     &setup.touch.move_distance,                 "touch.move_distance"
10557   },
10558   {
10559     TYPE_INTEGER,
10560     &setup.touch.drop_distance,                 "touch.drop_distance"
10561   },
10562   {
10563     TYPE_INTEGER,
10564     &setup.touch.transparency,                  "touch.transparency"
10565   },
10566   {
10567     TYPE_INTEGER,
10568     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10569   },
10570   {
10571     TYPE_INTEGER,
10572     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10573   },
10574   {
10575     TYPE_INTEGER,
10576     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10577   },
10578   {
10579     TYPE_INTEGER,
10580     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10581   },
10582   {
10583     TYPE_INTEGER,
10584     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10585   },
10586   {
10587     TYPE_INTEGER,
10588     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10589   },
10590   {
10591     TYPE_SWITCH,
10592     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10593   },
10594 };
10595
10596 static struct TokenInfo auto_setup_tokens[] =
10597 {
10598   {
10599     TYPE_INTEGER,
10600     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10601   },
10602 };
10603
10604 static struct TokenInfo server_setup_tokens[] =
10605 {
10606   {
10607     TYPE_STRING,
10608     &setup.player_uuid,                         "player_uuid"
10609   },
10610   {
10611     TYPE_INTEGER,
10612     &setup.player_version,                      "player_version"
10613   },
10614   {
10615     TYPE_SWITCH,
10616     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10617   },
10618   {
10619     TYPE_STRING,
10620     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10621   },
10622   {
10623     TYPE_STRING,
10624     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10625   },
10626   {
10627     TYPE_SWITCH,
10628     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10629   },
10630   {
10631     TYPE_SWITCH,
10632     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10645   },
10646 };
10647
10648 static struct TokenInfo editor_setup_tokens[] =
10649 {
10650   {
10651     TYPE_SWITCH,
10652     &setup.editor.el_classic,                   "editor.el_classic"
10653   },
10654   {
10655     TYPE_SWITCH,
10656     &setup.editor.el_custom,                    "editor.el_custom"
10657   },
10658   {
10659     TYPE_SWITCH,
10660     &setup.editor.el_user_defined,              "editor.el_user_defined"
10661   },
10662   {
10663     TYPE_SWITCH,
10664     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10665   },
10666   {
10667     TYPE_SWITCH,
10668     &setup.editor.el_headlines,                 "editor.el_headlines"
10669   },
10670   {
10671     TYPE_SWITCH,
10672     &setup.editor.show_element_token,           "editor.show_element_token"
10673   },
10674   {
10675     TYPE_SWITCH,
10676     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10677   },
10678 };
10679
10680 static struct TokenInfo editor_cascade_setup_tokens[] =
10681 {
10682   {
10683     TYPE_SWITCH,
10684     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10685   },
10686   {
10687     TYPE_SWITCH,
10688     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10689   },
10690   {
10691     TYPE_SWITCH,
10692     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10693   },
10694   {
10695     TYPE_SWITCH,
10696     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10697   },
10698   {
10699     TYPE_SWITCH,
10700     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10701   },
10702   {
10703     TYPE_SWITCH,
10704     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10705   },
10706   {
10707     TYPE_SWITCH,
10708     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10709   },
10710   {
10711     TYPE_SWITCH,
10712     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10713   },
10714   {
10715     TYPE_SWITCH,
10716     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10717   },
10718   {
10719     TYPE_SWITCH,
10720     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10721   },
10722   {
10723     TYPE_SWITCH,
10724     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10725   },
10726   {
10727     TYPE_SWITCH,
10728     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10729   },
10730   {
10731     TYPE_SWITCH,
10732     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10733   },
10734   {
10735     TYPE_SWITCH,
10736     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10737   },
10738   {
10739     TYPE_SWITCH,
10740     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10741   },
10742   {
10743     TYPE_SWITCH,
10744     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10745   },
10746   {
10747     TYPE_SWITCH,
10748     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10749   },
10750   {
10751     TYPE_SWITCH,
10752     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10753   },
10754   {
10755     TYPE_SWITCH,
10756     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10757   },
10758   {
10759     TYPE_SWITCH,
10760     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10761   },
10762 };
10763
10764 static struct TokenInfo shortcut_setup_tokens[] =
10765 {
10766   {
10767     TYPE_KEY_X11,
10768     &setup.shortcut.save_game,                  "shortcut.save_game"
10769   },
10770   {
10771     TYPE_KEY_X11,
10772     &setup.shortcut.load_game,                  "shortcut.load_game"
10773   },
10774   {
10775     TYPE_KEY_X11,
10776     &setup.shortcut.restart_game,               "shortcut.restart_game"
10777   },
10778   {
10779     TYPE_KEY_X11,
10780     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10781   },
10782   {
10783     TYPE_KEY_X11,
10784     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10785   },
10786   {
10787     TYPE_KEY_X11,
10788     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10789   },
10790   {
10791     TYPE_KEY_X11,
10792     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10793   },
10794   {
10795     TYPE_KEY_X11,
10796     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10797   },
10798   {
10799     TYPE_KEY_X11,
10800     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10801   },
10802   {
10803     TYPE_KEY_X11,
10804     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10805   },
10806   {
10807     TYPE_KEY_X11,
10808     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10809   },
10810   {
10811     TYPE_KEY_X11,
10812     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10813   },
10814   {
10815     TYPE_KEY_X11,
10816     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10817   },
10818   {
10819     TYPE_KEY_X11,
10820     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10821   },
10822   {
10823     TYPE_KEY_X11,
10824     &setup.shortcut.tape_record,                "shortcut.tape_record"
10825   },
10826   {
10827     TYPE_KEY_X11,
10828     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10829   },
10830   {
10831     TYPE_KEY_X11,
10832     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10833   },
10834   {
10835     TYPE_KEY_X11,
10836     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10837   },
10838   {
10839     TYPE_KEY_X11,
10840     &setup.shortcut.sound_music,                "shortcut.sound_music"
10841   },
10842   {
10843     TYPE_KEY_X11,
10844     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10845   },
10846   {
10847     TYPE_KEY_X11,
10848     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10849   },
10850   {
10851     TYPE_KEY_X11,
10852     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10853   },
10854   {
10855     TYPE_KEY_X11,
10856     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10857   },
10858 };
10859
10860 static struct SetupInputInfo setup_input;
10861 static struct TokenInfo player_setup_tokens[] =
10862 {
10863   {
10864     TYPE_BOOLEAN,
10865     &setup_input.use_joystick,                  ".use_joystick"
10866   },
10867   {
10868     TYPE_STRING,
10869     &setup_input.joy.device_name,               ".joy.device_name"
10870   },
10871   {
10872     TYPE_INTEGER,
10873     &setup_input.joy.xleft,                     ".joy.xleft"
10874   },
10875   {
10876     TYPE_INTEGER,
10877     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10878   },
10879   {
10880     TYPE_INTEGER,
10881     &setup_input.joy.xright,                    ".joy.xright"
10882   },
10883   {
10884     TYPE_INTEGER,
10885     &setup_input.joy.yupper,                    ".joy.yupper"
10886   },
10887   {
10888     TYPE_INTEGER,
10889     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10890   },
10891   {
10892     TYPE_INTEGER,
10893     &setup_input.joy.ylower,                    ".joy.ylower"
10894   },
10895   {
10896     TYPE_INTEGER,
10897     &setup_input.joy.snap,                      ".joy.snap_field"
10898   },
10899   {
10900     TYPE_INTEGER,
10901     &setup_input.joy.drop,                      ".joy.place_bomb"
10902   },
10903   {
10904     TYPE_KEY_X11,
10905     &setup_input.key.left,                      ".key.move_left"
10906   },
10907   {
10908     TYPE_KEY_X11,
10909     &setup_input.key.right,                     ".key.move_right"
10910   },
10911   {
10912     TYPE_KEY_X11,
10913     &setup_input.key.up,                        ".key.move_up"
10914   },
10915   {
10916     TYPE_KEY_X11,
10917     &setup_input.key.down,                      ".key.move_down"
10918   },
10919   {
10920     TYPE_KEY_X11,
10921     &setup_input.key.snap,                      ".key.snap_field"
10922   },
10923   {
10924     TYPE_KEY_X11,
10925     &setup_input.key.drop,                      ".key.place_bomb"
10926   },
10927 };
10928
10929 static struct TokenInfo system_setup_tokens[] =
10930 {
10931   {
10932     TYPE_STRING,
10933     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10934   },
10935   {
10936     TYPE_STRING,
10937     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10938   },
10939   {
10940     TYPE_STRING,
10941     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10942   },
10943   {
10944     TYPE_INTEGER,
10945     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10946   },
10947 };
10948
10949 static struct TokenInfo internal_setup_tokens[] =
10950 {
10951   {
10952     TYPE_STRING,
10953     &setup.internal.program_title,              "program_title"
10954   },
10955   {
10956     TYPE_STRING,
10957     &setup.internal.program_version,            "program_version"
10958   },
10959   {
10960     TYPE_STRING,
10961     &setup.internal.program_author,             "program_author"
10962   },
10963   {
10964     TYPE_STRING,
10965     &setup.internal.program_email,              "program_email"
10966   },
10967   {
10968     TYPE_STRING,
10969     &setup.internal.program_website,            "program_website"
10970   },
10971   {
10972     TYPE_STRING,
10973     &setup.internal.program_copyright,          "program_copyright"
10974   },
10975   {
10976     TYPE_STRING,
10977     &setup.internal.program_company,            "program_company"
10978   },
10979   {
10980     TYPE_STRING,
10981     &setup.internal.program_icon_file,          "program_icon_file"
10982   },
10983   {
10984     TYPE_STRING,
10985     &setup.internal.default_graphics_set,       "default_graphics_set"
10986   },
10987   {
10988     TYPE_STRING,
10989     &setup.internal.default_sounds_set,         "default_sounds_set"
10990   },
10991   {
10992     TYPE_STRING,
10993     &setup.internal.default_music_set,          "default_music_set"
10994   },
10995   {
10996     TYPE_STRING,
10997     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10998   },
10999   {
11000     TYPE_STRING,
11001     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11002   },
11003   {
11004     TYPE_STRING,
11005     &setup.internal.fallback_music_file,        "fallback_music_file"
11006   },
11007   {
11008     TYPE_STRING,
11009     &setup.internal.default_level_series,       "default_level_series"
11010   },
11011   {
11012     TYPE_INTEGER,
11013     &setup.internal.default_window_width,       "default_window_width"
11014   },
11015   {
11016     TYPE_INTEGER,
11017     &setup.internal.default_window_height,      "default_window_height"
11018   },
11019   {
11020     TYPE_BOOLEAN,
11021     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11022   },
11023   {
11024     TYPE_BOOLEAN,
11025     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11026   },
11027   {
11028     TYPE_BOOLEAN,
11029     &setup.internal.create_user_levelset,       "create_user_levelset"
11030   },
11031   {
11032     TYPE_BOOLEAN,
11033     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11034   },
11035   {
11036     TYPE_BOOLEAN,
11037     &setup.internal.menu_game,                  "menu_game"
11038   },
11039   {
11040     TYPE_BOOLEAN,
11041     &setup.internal.menu_engines,               "menu_engines"
11042   },
11043   {
11044     TYPE_BOOLEAN,
11045     &setup.internal.menu_editor,                "menu_editor"
11046   },
11047   {
11048     TYPE_BOOLEAN,
11049     &setup.internal.menu_graphics,              "menu_graphics"
11050   },
11051   {
11052     TYPE_BOOLEAN,
11053     &setup.internal.menu_sound,                 "menu_sound"
11054   },
11055   {
11056     TYPE_BOOLEAN,
11057     &setup.internal.menu_artwork,               "menu_artwork"
11058   },
11059   {
11060     TYPE_BOOLEAN,
11061     &setup.internal.menu_input,                 "menu_input"
11062   },
11063   {
11064     TYPE_BOOLEAN,
11065     &setup.internal.menu_touch,                 "menu_touch"
11066   },
11067   {
11068     TYPE_BOOLEAN,
11069     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11070   },
11071   {
11072     TYPE_BOOLEAN,
11073     &setup.internal.menu_exit,                  "menu_exit"
11074   },
11075   {
11076     TYPE_BOOLEAN,
11077     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11078   },
11079   {
11080     TYPE_BOOLEAN,
11081     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11082   },
11083   {
11084     TYPE_BOOLEAN,
11085     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11086   },
11087   {
11088     TYPE_BOOLEAN,
11089     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11090   },
11091   {
11092     TYPE_BOOLEAN,
11093     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11094   },
11095   {
11096     TYPE_BOOLEAN,
11097     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11098   },
11099   {
11100     TYPE_BOOLEAN,
11101     &setup.internal.info_title,                 "info_title"
11102   },
11103   {
11104     TYPE_BOOLEAN,
11105     &setup.internal.info_elements,              "info_elements"
11106   },
11107   {
11108     TYPE_BOOLEAN,
11109     &setup.internal.info_music,                 "info_music"
11110   },
11111   {
11112     TYPE_BOOLEAN,
11113     &setup.internal.info_credits,               "info_credits"
11114   },
11115   {
11116     TYPE_BOOLEAN,
11117     &setup.internal.info_program,               "info_program"
11118   },
11119   {
11120     TYPE_BOOLEAN,
11121     &setup.internal.info_version,               "info_version"
11122   },
11123   {
11124     TYPE_BOOLEAN,
11125     &setup.internal.info_levelset,              "info_levelset"
11126   },
11127   {
11128     TYPE_BOOLEAN,
11129     &setup.internal.info_exit,                  "info_exit"
11130   },
11131 };
11132
11133 static struct TokenInfo debug_setup_tokens[] =
11134 {
11135   {
11136     TYPE_INTEGER,
11137     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11138   },
11139   {
11140     TYPE_INTEGER,
11141     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11142   },
11143   {
11144     TYPE_INTEGER,
11145     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11146   },
11147   {
11148     TYPE_INTEGER,
11149     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11150   },
11151   {
11152     TYPE_INTEGER,
11153     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11154   },
11155   {
11156     TYPE_INTEGER,
11157     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11158   },
11159   {
11160     TYPE_INTEGER,
11161     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11162   },
11163   {
11164     TYPE_INTEGER,
11165     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11166   },
11167   {
11168     TYPE_INTEGER,
11169     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11170   },
11171   {
11172     TYPE_INTEGER,
11173     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11174   },
11175   {
11176     TYPE_KEY_X11,
11177     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11178   },
11179   {
11180     TYPE_KEY_X11,
11181     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11206   },
11207   {
11208     TYPE_KEY_X11,
11209     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11210   },
11211   {
11212     TYPE_KEY_X11,
11213     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11214   },
11215   {
11216     TYPE_BOOLEAN,
11217     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11218   {
11219     TYPE_BOOLEAN,
11220     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11221   },
11222   {
11223     TYPE_BOOLEAN,
11224     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11225   },
11226   {
11227     TYPE_SWITCH3,
11228     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11229   },
11230   {
11231     TYPE_INTEGER,
11232     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11233   },
11234 };
11235
11236 static struct TokenInfo options_setup_tokens[] =
11237 {
11238   {
11239     TYPE_BOOLEAN,
11240     &setup.options.verbose,                     "options.verbose"
11241   },
11242   {
11243     TYPE_BOOLEAN,
11244     &setup.options.debug,                       "options.debug"
11245   },
11246   {
11247     TYPE_STRING,
11248     &setup.options.debug_mode,                  "options.debug_mode"
11249   },
11250 };
11251
11252 static void setSetupInfoToDefaults(struct SetupInfo *si)
11253 {
11254   int i;
11255
11256   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11257
11258   si->multiple_users = TRUE;
11259
11260   si->sound = TRUE;
11261   si->sound_loops = TRUE;
11262   si->sound_music = TRUE;
11263   si->sound_simple = TRUE;
11264   si->toons = TRUE;
11265   si->global_animations = TRUE;
11266   si->scroll_delay = TRUE;
11267   si->forced_scroll_delay = FALSE;
11268   si->scroll_delay_value = STD_SCROLL_DELAY;
11269   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11270   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11271   si->fade_screens = TRUE;
11272   si->autorecord = TRUE;
11273   si->autorecord_after_replay = TRUE;
11274   si->auto_pause_on_start = FALSE;
11275   si->show_titlescreen = TRUE;
11276   si->quick_doors = FALSE;
11277   si->team_mode = FALSE;
11278   si->handicap = TRUE;
11279   si->skip_levels = TRUE;
11280   si->increment_levels = TRUE;
11281   si->auto_play_next_level = TRUE;
11282   si->count_score_after_game = TRUE;
11283   si->show_scores_after_game = TRUE;
11284   si->time_limit = TRUE;
11285   si->fullscreen = FALSE;
11286   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11287   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11288   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11289   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11290   si->ask_on_escape = TRUE;
11291   si->ask_on_escape_editor = TRUE;
11292   si->ask_on_game_over = TRUE;
11293   si->ask_on_quit_game = TRUE;
11294   si->ask_on_quit_program = TRUE;
11295   si->quick_switch = FALSE;
11296   si->input_on_focus = FALSE;
11297   si->prefer_aga_graphics = TRUE;
11298   si->prefer_lowpass_sounds = FALSE;
11299   si->prefer_extra_panel_items = TRUE;
11300   si->game_speed_extended = FALSE;
11301   si->game_frame_delay = GAME_FRAME_DELAY;
11302   si->bd_skip_uncovering = FALSE;
11303   si->bd_skip_hatching = FALSE;
11304   si->bd_scroll_delay = TRUE;
11305   si->bd_smooth_movements = AUTO;
11306   si->sp_show_border_elements = FALSE;
11307   si->small_game_graphics = FALSE;
11308   si->show_load_save_buttons = FALSE;
11309   si->show_undo_redo_buttons = FALSE;
11310   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11311
11312   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11313   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11314   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11315
11316   si->override_level_graphics = FALSE;
11317   si->override_level_sounds = FALSE;
11318   si->override_level_music = FALSE;
11319
11320   si->volume_simple = 100;              // percent
11321   si->volume_loops = 100;               // percent
11322   si->volume_music = 100;               // percent
11323
11324   si->network_mode = FALSE;
11325   si->network_player_nr = 0;            // first player
11326   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11327
11328   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11329   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11330   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11331   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11332   si->touch.draw_outlined = TRUE;
11333   si->touch.draw_pressed = TRUE;
11334
11335   for (i = 0; i < 2; i++)
11336   {
11337     char *default_grid_button[6][2] =
11338     {
11339       { "      ", "  ^^  " },
11340       { "      ", "  ^^  " },
11341       { "      ", "<<  >>" },
11342       { "      ", "<<  >>" },
11343       { "111222", "  vv  " },
11344       { "111222", "  vv  " }
11345     };
11346     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11347     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11348     int min_xsize = MIN(6, grid_xsize);
11349     int min_ysize = MIN(6, grid_ysize);
11350     int startx = grid_xsize - min_xsize;
11351     int starty = grid_ysize - min_ysize;
11352     int x, y;
11353
11354     // virtual buttons grid can only be set to defaults if video is initialized
11355     // (this will be repeated if virtual buttons are not loaded from setup file)
11356     if (video.initialized)
11357     {
11358       si->touch.grid_xsize[i] = grid_xsize;
11359       si->touch.grid_ysize[i] = grid_ysize;
11360     }
11361     else
11362     {
11363       si->touch.grid_xsize[i] = -1;
11364       si->touch.grid_ysize[i] = -1;
11365     }
11366
11367     for (x = 0; x < MAX_GRID_XSIZE; x++)
11368       for (y = 0; y < MAX_GRID_YSIZE; y++)
11369         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11370
11371     for (x = 0; x < min_xsize; x++)
11372       for (y = 0; y < min_ysize; y++)
11373         si->touch.grid_button[i][x][starty + y] =
11374           default_grid_button[y][0][x];
11375
11376     for (x = 0; x < min_xsize; x++)
11377       for (y = 0; y < min_ysize; y++)
11378         si->touch.grid_button[i][startx + x][starty + y] =
11379           default_grid_button[y][1][x];
11380   }
11381
11382   si->touch.grid_initialized            = video.initialized;
11383
11384   si->touch.overlay_buttons             = FALSE;
11385
11386   si->editor.el_boulderdash             = TRUE;
11387   si->editor.el_boulderdash_native      = TRUE;
11388   si->editor.el_boulderdash_effects     = TRUE;
11389   si->editor.el_emerald_mine            = TRUE;
11390   si->editor.el_emerald_mine_club       = TRUE;
11391   si->editor.el_more                    = TRUE;
11392   si->editor.el_sokoban                 = TRUE;
11393   si->editor.el_supaplex                = TRUE;
11394   si->editor.el_diamond_caves           = TRUE;
11395   si->editor.el_dx_boulderdash          = TRUE;
11396
11397   si->editor.el_mirror_magic            = TRUE;
11398   si->editor.el_deflektor               = TRUE;
11399
11400   si->editor.el_chars                   = TRUE;
11401   si->editor.el_steel_chars             = TRUE;
11402
11403   si->editor.el_classic                 = TRUE;
11404   si->editor.el_custom                  = TRUE;
11405
11406   si->editor.el_user_defined            = FALSE;
11407   si->editor.el_dynamic                 = TRUE;
11408
11409   si->editor.el_headlines               = TRUE;
11410
11411   si->editor.show_element_token         = FALSE;
11412
11413   si->editor.show_read_only_warning     = TRUE;
11414
11415   si->editor.use_template_for_new_levels = TRUE;
11416
11417   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11418   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11419   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11420   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11421   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11422
11423   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11424   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11425   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11426   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11427   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11428
11429   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11430   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11431   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11432   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11433   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11434   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11435
11436   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11437   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11438   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11439
11440   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11441   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11442   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11443   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11444
11445   for (i = 0; i < MAX_PLAYERS; i++)
11446   {
11447     si->input[i].use_joystick = FALSE;
11448     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11449     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11450     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11451     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11452     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11453     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11454     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11455     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11456     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11457     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11458     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11459     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11460     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11461     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11462     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11463   }
11464
11465   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11466   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11467   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11468   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11469
11470   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11471   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11472   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11473   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11474   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11475   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11476   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11477
11478   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11479
11480   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11481   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11482   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11483
11484   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11485   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11486   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11487
11488   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11489   si->internal.choose_from_top_leveldir = FALSE;
11490   si->internal.show_scaling_in_title = TRUE;
11491   si->internal.create_user_levelset = TRUE;
11492   si->internal.info_screens_from_main = FALSE;
11493
11494   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11495   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11496
11497   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11498   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11499   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11500   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11501   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11502   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11503   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11504   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11505   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11506   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11507
11508   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11509   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11510   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11511   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11512   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11513   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11514   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11515   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11516   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11517   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11518
11519   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11520   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11521
11522   si->debug.show_frames_per_second = FALSE;
11523
11524   si->debug.xsn_mode = AUTO;
11525   si->debug.xsn_percent = 0;
11526
11527   si->options.verbose = FALSE;
11528   si->options.debug = FALSE;
11529   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11530
11531 #if defined(PLATFORM_ANDROID)
11532   si->fullscreen = TRUE;
11533   si->touch.overlay_buttons = TRUE;
11534 #endif
11535
11536   setHideSetupEntry(&setup.debug.xsn_mode);
11537 }
11538
11539 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11540 {
11541   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11542 }
11543
11544 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11545 {
11546   si->player_uuid = NULL;       // (will be set later)
11547   si->player_version = 1;       // (will be set later)
11548
11549   si->use_api_server = TRUE;
11550   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11551   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11552   si->ask_for_uploading_tapes = TRUE;
11553   si->ask_for_remaining_tapes = FALSE;
11554   si->provide_uploading_tapes = TRUE;
11555   si->ask_for_using_api_server = TRUE;
11556   si->has_remaining_tapes = FALSE;
11557 }
11558
11559 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11560 {
11561   si->editor_cascade.el_bd              = TRUE;
11562   si->editor_cascade.el_bd_native       = TRUE;
11563   si->editor_cascade.el_bd_effects      = FALSE;
11564   si->editor_cascade.el_em              = TRUE;
11565   si->editor_cascade.el_emc             = TRUE;
11566   si->editor_cascade.el_rnd             = TRUE;
11567   si->editor_cascade.el_sb              = TRUE;
11568   si->editor_cascade.el_sp              = TRUE;
11569   si->editor_cascade.el_dc              = TRUE;
11570   si->editor_cascade.el_dx              = TRUE;
11571
11572   si->editor_cascade.el_mm              = TRUE;
11573   si->editor_cascade.el_df              = TRUE;
11574
11575   si->editor_cascade.el_chars           = FALSE;
11576   si->editor_cascade.el_steel_chars     = FALSE;
11577   si->editor_cascade.el_ce              = FALSE;
11578   si->editor_cascade.el_ge              = FALSE;
11579   si->editor_cascade.el_es              = FALSE;
11580   si->editor_cascade.el_ref             = FALSE;
11581   si->editor_cascade.el_user            = FALSE;
11582   si->editor_cascade.el_dynamic         = FALSE;
11583 }
11584
11585 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11586
11587 static char *getHideSetupToken(void *setup_value)
11588 {
11589   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11590
11591   if (setup_value != NULL)
11592     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11593
11594   return hide_setup_token;
11595 }
11596
11597 void setHideSetupEntry(void *setup_value)
11598 {
11599   char *hide_setup_token = getHideSetupToken(setup_value);
11600
11601   if (hide_setup_hash == NULL)
11602     hide_setup_hash = newSetupFileHash();
11603
11604   if (setup_value != NULL)
11605     setHashEntry(hide_setup_hash, hide_setup_token, "");
11606 }
11607
11608 void removeHideSetupEntry(void *setup_value)
11609 {
11610   char *hide_setup_token = getHideSetupToken(setup_value);
11611
11612   if (setup_value != NULL)
11613     removeHashEntry(hide_setup_hash, hide_setup_token);
11614 }
11615
11616 boolean hideSetupEntry(void *setup_value)
11617 {
11618   char *hide_setup_token = getHideSetupToken(setup_value);
11619
11620   return (setup_value != NULL &&
11621           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11622 }
11623
11624 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11625                                       struct TokenInfo *token_info,
11626                                       int token_nr, char *token_text)
11627 {
11628   char *token_hide_text = getStringCat2(token_text, ".hide");
11629   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11630
11631   // set the value of this setup option in the setup option structure
11632   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11633
11634   // check if this setup option should be hidden in the setup menu
11635   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11636     setHideSetupEntry(token_info[token_nr].value);
11637
11638   free(token_hide_text);
11639 }
11640
11641 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11642                                       struct TokenInfo *token_info,
11643                                       int token_nr)
11644 {
11645   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11646                             token_info[token_nr].text);
11647 }
11648
11649 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11650 {
11651   int i, pnr;
11652
11653   if (!setup_file_hash)
11654     return;
11655
11656   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11657     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11658
11659   setup.touch.grid_initialized = TRUE;
11660   for (i = 0; i < 2; i++)
11661   {
11662     int grid_xsize = setup.touch.grid_xsize[i];
11663     int grid_ysize = setup.touch.grid_ysize[i];
11664     int x, y;
11665
11666     // if virtual buttons are not loaded from setup file, repeat initializing
11667     // virtual buttons grid with default values later when video is initialized
11668     if (grid_xsize == -1 ||
11669         grid_ysize == -1)
11670     {
11671       setup.touch.grid_initialized = FALSE;
11672
11673       continue;
11674     }
11675
11676     for (y = 0; y < grid_ysize; y++)
11677     {
11678       char token_string[MAX_LINE_LEN];
11679
11680       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11681
11682       char *value_string = getHashEntry(setup_file_hash, token_string);
11683
11684       if (value_string == NULL)
11685         continue;
11686
11687       for (x = 0; x < grid_xsize; x++)
11688       {
11689         char c = value_string[x];
11690
11691         setup.touch.grid_button[i][x][y] =
11692           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11693       }
11694     }
11695   }
11696
11697   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11698     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11699
11700   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11701     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11702
11703   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11704   {
11705     char prefix[30];
11706
11707     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11708
11709     setup_input = setup.input[pnr];
11710     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11711     {
11712       char full_token[100];
11713
11714       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11715       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11716                                 full_token);
11717     }
11718     setup.input[pnr] = setup_input;
11719   }
11720
11721   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11722     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11723
11724   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11725     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11726
11727   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11728     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11729
11730   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11731     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11732
11733   setHideRelatedSetupEntries();
11734 }
11735
11736 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11737 {
11738   int i;
11739
11740   if (!setup_file_hash)
11741     return;
11742
11743   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11744     setSetupInfo(auto_setup_tokens, i,
11745                  getHashEntry(setup_file_hash,
11746                               auto_setup_tokens[i].text));
11747 }
11748
11749 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11750 {
11751   int i;
11752
11753   if (!setup_file_hash)
11754     return;
11755
11756   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11757     setSetupInfo(server_setup_tokens, i,
11758                  getHashEntry(setup_file_hash,
11759                               server_setup_tokens[i].text));
11760 }
11761
11762 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11763 {
11764   int i;
11765
11766   if (!setup_file_hash)
11767     return;
11768
11769   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11770     setSetupInfo(editor_cascade_setup_tokens, i,
11771                  getHashEntry(setup_file_hash,
11772                               editor_cascade_setup_tokens[i].text));
11773 }
11774
11775 void LoadUserNames(void)
11776 {
11777   int last_user_nr = user.nr;
11778   int i;
11779
11780   if (global.user_names != NULL)
11781   {
11782     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11783       checked_free(global.user_names[i]);
11784
11785     checked_free(global.user_names);
11786   }
11787
11788   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11789
11790   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11791   {
11792     user.nr = i;
11793
11794     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11795
11796     if (setup_file_hash)
11797     {
11798       char *player_name = getHashEntry(setup_file_hash, "player_name");
11799
11800       global.user_names[i] = getFixedUserName(player_name);
11801
11802       freeSetupFileHash(setup_file_hash);
11803     }
11804
11805     if (global.user_names[i] == NULL)
11806       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11807   }
11808
11809   user.nr = last_user_nr;
11810 }
11811
11812 void LoadSetupFromFilename(char *filename)
11813 {
11814   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11815
11816   if (setup_file_hash)
11817   {
11818     decodeSetupFileHash_Default(setup_file_hash);
11819
11820     freeSetupFileHash(setup_file_hash);
11821   }
11822   else
11823   {
11824     Debug("setup", "using default setup values");
11825   }
11826 }
11827
11828 static void LoadSetup_SpecialPostProcessing(void)
11829 {
11830   char *player_name_new;
11831
11832   // needed to work around problems with fixed length strings
11833   player_name_new = getFixedUserName(setup.player_name);
11834   free(setup.player_name);
11835   setup.player_name = player_name_new;
11836
11837   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11838   if (setup.scroll_delay == FALSE)
11839   {
11840     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11841     setup.scroll_delay = TRUE;                  // now always "on"
11842   }
11843
11844   // make sure that scroll delay value stays inside valid range
11845   setup.scroll_delay_value =
11846     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11847 }
11848
11849 void LoadSetup_Default(void)
11850 {
11851   char *filename;
11852
11853   // always start with reliable default values
11854   setSetupInfoToDefaults(&setup);
11855
11856   // try to load setup values from default setup file
11857   filename = getDefaultSetupFilename();
11858
11859   if (fileExists(filename))
11860     LoadSetupFromFilename(filename);
11861
11862   // try to load setup values from platform setup file
11863   filename = getPlatformSetupFilename();
11864
11865   if (fileExists(filename))
11866     LoadSetupFromFilename(filename);
11867
11868   // try to load setup values from user setup file
11869   filename = getSetupFilename();
11870
11871   LoadSetupFromFilename(filename);
11872
11873   LoadSetup_SpecialPostProcessing();
11874 }
11875
11876 void LoadSetup_AutoSetup(void)
11877 {
11878   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11879   SetupFileHash *setup_file_hash = NULL;
11880
11881   // always start with reliable default values
11882   setSetupInfoToDefaults_AutoSetup(&setup);
11883
11884   setup_file_hash = loadSetupFileHash(filename);
11885
11886   if (setup_file_hash)
11887   {
11888     decodeSetupFileHash_AutoSetup(setup_file_hash);
11889
11890     freeSetupFileHash(setup_file_hash);
11891   }
11892
11893   free(filename);
11894 }
11895
11896 void LoadSetup_ServerSetup(void)
11897 {
11898   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11899   SetupFileHash *setup_file_hash = NULL;
11900
11901   // always start with reliable default values
11902   setSetupInfoToDefaults_ServerSetup(&setup);
11903
11904   setup_file_hash = loadSetupFileHash(filename);
11905
11906   if (setup_file_hash)
11907   {
11908     decodeSetupFileHash_ServerSetup(setup_file_hash);
11909
11910     freeSetupFileHash(setup_file_hash);
11911   }
11912
11913   free(filename);
11914
11915   if (setup.player_uuid == NULL)
11916   {
11917     // player UUID does not yet exist in setup file
11918     setup.player_uuid = getStringCopy(getUUID());
11919     setup.player_version = 2;
11920
11921     SaveSetup_ServerSetup();
11922   }
11923 }
11924
11925 void LoadSetup_EditorCascade(void)
11926 {
11927   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11928   SetupFileHash *setup_file_hash = NULL;
11929
11930   // always start with reliable default values
11931   setSetupInfoToDefaults_EditorCascade(&setup);
11932
11933   setup_file_hash = loadSetupFileHash(filename);
11934
11935   if (setup_file_hash)
11936   {
11937     decodeSetupFileHash_EditorCascade(setup_file_hash);
11938
11939     freeSetupFileHash(setup_file_hash);
11940   }
11941
11942   free(filename);
11943 }
11944
11945 void LoadSetup(void)
11946 {
11947   LoadSetup_Default();
11948   LoadSetup_AutoSetup();
11949   LoadSetup_ServerSetup();
11950   LoadSetup_EditorCascade();
11951 }
11952
11953 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11954                                            char *mapping_line)
11955 {
11956   char mapping_guid[MAX_LINE_LEN];
11957   char *mapping_start, *mapping_end;
11958
11959   // get GUID from game controller mapping line: copy complete line
11960   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11961   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11962
11963   // get GUID from game controller mapping line: cut after GUID part
11964   mapping_start = strchr(mapping_guid, ',');
11965   if (mapping_start != NULL)
11966     *mapping_start = '\0';
11967
11968   // cut newline from game controller mapping line
11969   mapping_end = strchr(mapping_line, '\n');
11970   if (mapping_end != NULL)
11971     *mapping_end = '\0';
11972
11973   // add mapping entry to game controller mappings hash
11974   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11975 }
11976
11977 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11978                                                  char *filename)
11979 {
11980   FILE *file;
11981
11982   if (!(file = fopen(filename, MODE_READ)))
11983   {
11984     Warn("cannot read game controller mappings file '%s'", filename);
11985
11986     return;
11987   }
11988
11989   while (!feof(file))
11990   {
11991     char line[MAX_LINE_LEN];
11992
11993     if (!fgets(line, MAX_LINE_LEN, file))
11994       break;
11995
11996     addGameControllerMappingToHash(mappings_hash, line);
11997   }
11998
11999   fclose(file);
12000 }
12001
12002 void SaveSetup_Default(void)
12003 {
12004   char *filename = getSetupFilename();
12005   FILE *file;
12006   int i, pnr;
12007
12008   InitUserDataDirectory();
12009
12010   if (!(file = fopen(filename, MODE_WRITE)))
12011   {
12012     Warn("cannot write setup file '%s'", filename);
12013
12014     return;
12015   }
12016
12017   fprintFileHeader(file, SETUP_FILENAME);
12018
12019   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12020   {
12021     // just to make things nicer :)
12022     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12023         global_setup_tokens[i].value == &setup.sound                    ||
12024         global_setup_tokens[i].value == &setup.graphics_set             ||
12025         global_setup_tokens[i].value == &setup.volume_simple            ||
12026         global_setup_tokens[i].value == &setup.network_mode             ||
12027         global_setup_tokens[i].value == &setup.touch.control_type       ||
12028         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12029         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12030       fprintf(file, "\n");
12031
12032     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12033   }
12034
12035   for (i = 0; i < 2; i++)
12036   {
12037     int grid_xsize = setup.touch.grid_xsize[i];
12038     int grid_ysize = setup.touch.grid_ysize[i];
12039     int x, y;
12040
12041     fprintf(file, "\n");
12042
12043     for (y = 0; y < grid_ysize; y++)
12044     {
12045       char token_string[MAX_LINE_LEN];
12046       char value_string[MAX_LINE_LEN];
12047
12048       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12049
12050       for (x = 0; x < grid_xsize; x++)
12051       {
12052         char c = setup.touch.grid_button[i][x][y];
12053
12054         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12055       }
12056
12057       value_string[grid_xsize] = '\0';
12058
12059       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12060     }
12061   }
12062
12063   fprintf(file, "\n");
12064   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12065     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12066
12067   fprintf(file, "\n");
12068   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12069     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12070
12071   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12072   {
12073     char prefix[30];
12074
12075     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12076     fprintf(file, "\n");
12077
12078     setup_input = setup.input[pnr];
12079     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12080       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12081   }
12082
12083   fprintf(file, "\n");
12084   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12085     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12086
12087   // (internal setup values not saved to user setup file)
12088
12089   fprintf(file, "\n");
12090   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12091     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12092         setup.debug.xsn_mode != AUTO)
12093       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12094
12095   fprintf(file, "\n");
12096   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12097     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12098
12099   fclose(file);
12100
12101   SetFilePermissions(filename, PERMS_PRIVATE);
12102 }
12103
12104 void SaveSetup_AutoSetup(void)
12105 {
12106   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12107   FILE *file;
12108   int i;
12109
12110   InitUserDataDirectory();
12111
12112   if (!(file = fopen(filename, MODE_WRITE)))
12113   {
12114     Warn("cannot write auto setup file '%s'", filename);
12115
12116     free(filename);
12117
12118     return;
12119   }
12120
12121   fprintFileHeader(file, AUTOSETUP_FILENAME);
12122
12123   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12124     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12125
12126   fclose(file);
12127
12128   SetFilePermissions(filename, PERMS_PRIVATE);
12129
12130   free(filename);
12131 }
12132
12133 void SaveSetup_ServerSetup(void)
12134 {
12135   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12136   FILE *file;
12137   int i;
12138
12139   InitUserDataDirectory();
12140
12141   if (!(file = fopen(filename, MODE_WRITE)))
12142   {
12143     Warn("cannot write server setup file '%s'", filename);
12144
12145     free(filename);
12146
12147     return;
12148   }
12149
12150   fprintFileHeader(file, SERVERSETUP_FILENAME);
12151
12152   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12153   {
12154     // just to make things nicer :)
12155     if (server_setup_tokens[i].value == &setup.use_api_server)
12156       fprintf(file, "\n");
12157
12158     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12159   }
12160
12161   fclose(file);
12162
12163   SetFilePermissions(filename, PERMS_PRIVATE);
12164
12165   free(filename);
12166 }
12167
12168 void SaveSetup_EditorCascade(void)
12169 {
12170   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12171   FILE *file;
12172   int i;
12173
12174   InitUserDataDirectory();
12175
12176   if (!(file = fopen(filename, MODE_WRITE)))
12177   {
12178     Warn("cannot write editor cascade state file '%s'", filename);
12179
12180     free(filename);
12181
12182     return;
12183   }
12184
12185   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12186
12187   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12188     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12189
12190   fclose(file);
12191
12192   SetFilePermissions(filename, PERMS_PRIVATE);
12193
12194   free(filename);
12195 }
12196
12197 void SaveSetup(void)
12198 {
12199   SaveSetup_Default();
12200   SaveSetup_AutoSetup();
12201   SaveSetup_ServerSetup();
12202   SaveSetup_EditorCascade();
12203 }
12204
12205 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12206                                                   char *filename)
12207 {
12208   FILE *file;
12209
12210   if (!(file = fopen(filename, MODE_WRITE)))
12211   {
12212     Warn("cannot write game controller mappings file '%s'", filename);
12213
12214     return;
12215   }
12216
12217   BEGIN_HASH_ITERATION(mappings_hash, itr)
12218   {
12219     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12220   }
12221   END_HASH_ITERATION(mappings_hash, itr)
12222
12223   fclose(file);
12224 }
12225
12226 void SaveSetup_AddGameControllerMapping(char *mapping)
12227 {
12228   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12229   SetupFileHash *mappings_hash = newSetupFileHash();
12230
12231   InitUserDataDirectory();
12232
12233   // load existing personal game controller mappings
12234   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12235
12236   // add new mapping to personal game controller mappings
12237   addGameControllerMappingToHash(mappings_hash, mapping);
12238
12239   // save updated personal game controller mappings
12240   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12241
12242   freeSetupFileHash(mappings_hash);
12243   free(filename);
12244 }
12245
12246 void LoadCustomElementDescriptions(void)
12247 {
12248   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12249   SetupFileHash *setup_file_hash;
12250   int i;
12251
12252   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12253   {
12254     if (element_info[i].custom_description != NULL)
12255     {
12256       free(element_info[i].custom_description);
12257       element_info[i].custom_description = NULL;
12258     }
12259   }
12260
12261   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12262     return;
12263
12264   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12265   {
12266     char *token = getStringCat2(element_info[i].token_name, ".name");
12267     char *value = getHashEntry(setup_file_hash, token);
12268
12269     if (value != NULL)
12270       element_info[i].custom_description = getStringCopy(value);
12271
12272     free(token);
12273   }
12274
12275   freeSetupFileHash(setup_file_hash);
12276 }
12277
12278 static int getElementFromToken(char *token)
12279 {
12280   char *value = getHashEntry(element_token_hash, token);
12281
12282   if (value != NULL)
12283     return atoi(value);
12284
12285   Warn("unknown element token '%s'", token);
12286
12287   return EL_UNDEFINED;
12288 }
12289
12290 void FreeGlobalAnimEventInfo(void)
12291 {
12292   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12293
12294   if (gaei->event_list == NULL)
12295     return;
12296
12297   int i;
12298
12299   for (i = 0; i < gaei->num_event_lists; i++)
12300   {
12301     checked_free(gaei->event_list[i]->event_value);
12302     checked_free(gaei->event_list[i]);
12303   }
12304
12305   checked_free(gaei->event_list);
12306
12307   gaei->event_list = NULL;
12308   gaei->num_event_lists = 0;
12309 }
12310
12311 static int AddGlobalAnimEventList(void)
12312 {
12313   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12314   int list_pos = gaei->num_event_lists++;
12315
12316   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12317                                      sizeof(struct GlobalAnimEventListInfo *));
12318
12319   gaei->event_list[list_pos] =
12320     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12321
12322   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12323
12324   gaeli->event_value = NULL;
12325   gaeli->num_event_values = 0;
12326
12327   return list_pos;
12328 }
12329
12330 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12331 {
12332   // do not add empty global animation events
12333   if (event_value == ANIM_EVENT_NONE)
12334     return list_pos;
12335
12336   // if list position is undefined, create new list
12337   if (list_pos == ANIM_EVENT_UNDEFINED)
12338     list_pos = AddGlobalAnimEventList();
12339
12340   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12341   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12342   int value_pos = gaeli->num_event_values++;
12343
12344   gaeli->event_value = checked_realloc(gaeli->event_value,
12345                                        gaeli->num_event_values * sizeof(int *));
12346
12347   gaeli->event_value[value_pos] = event_value;
12348
12349   return list_pos;
12350 }
12351
12352 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12353 {
12354   if (list_pos == ANIM_EVENT_UNDEFINED)
12355     return ANIM_EVENT_NONE;
12356
12357   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12358   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12359
12360   return gaeli->event_value[value_pos];
12361 }
12362
12363 int GetGlobalAnimEventValueCount(int list_pos)
12364 {
12365   if (list_pos == ANIM_EVENT_UNDEFINED)
12366     return 0;
12367
12368   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12369   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12370
12371   return gaeli->num_event_values;
12372 }
12373
12374 // This function checks if a string <s> of the format "string1, string2, ..."
12375 // exactly contains a string <s_contained>.
12376
12377 static boolean string_has_parameter(char *s, char *s_contained)
12378 {
12379   char *substring;
12380
12381   if (s == NULL || s_contained == NULL)
12382     return FALSE;
12383
12384   if (strlen(s_contained) > strlen(s))
12385     return FALSE;
12386
12387   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12388   {
12389     char next_char = s[strlen(s_contained)];
12390
12391     // check if next character is delimiter or whitespace
12392     if (next_char == ',' || next_char == '\0' ||
12393         next_char == ' ' || next_char == '\t')
12394       return TRUE;
12395   }
12396
12397   // check if string contains another parameter string after a comma
12398   substring = strchr(s, ',');
12399   if (substring == NULL)        // string does not contain a comma
12400     return FALSE;
12401
12402   // advance string pointer to next character after the comma
12403   substring++;
12404
12405   // skip potential whitespaces after the comma
12406   while (*substring == ' ' || *substring == '\t')
12407     substring++;
12408
12409   return string_has_parameter(substring, s_contained);
12410 }
12411
12412 static int get_anim_parameter_value_ce(char *s)
12413 {
12414   char *s_ptr = s;
12415   char *pattern_1 = "ce_change:custom_";
12416   char *pattern_2 = ".page_";
12417   int pattern_1_len = strlen(pattern_1);
12418   char *matching_char = strstr(s_ptr, pattern_1);
12419   int result = ANIM_EVENT_NONE;
12420
12421   if (matching_char == NULL)
12422     return ANIM_EVENT_NONE;
12423
12424   result = ANIM_EVENT_CE_CHANGE;
12425
12426   s_ptr = matching_char + pattern_1_len;
12427
12428   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12429   if (*s_ptr >= '0' && *s_ptr <= '9')
12430   {
12431     int gic_ce_nr = (*s_ptr++ - '0');
12432
12433     if (*s_ptr >= '0' && *s_ptr <= '9')
12434     {
12435       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12436
12437       if (*s_ptr >= '0' && *s_ptr <= '9')
12438         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12439     }
12440
12441     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12442       return ANIM_EVENT_NONE;
12443
12444     // custom element stored as 0 to 255
12445     gic_ce_nr--;
12446
12447     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12448   }
12449   else
12450   {
12451     // invalid custom element number specified
12452
12453     return ANIM_EVENT_NONE;
12454   }
12455
12456   // check for change page number ("page_X" or "page_XX") (optional)
12457   if (strPrefix(s_ptr, pattern_2))
12458   {
12459     s_ptr += strlen(pattern_2);
12460
12461     if (*s_ptr >= '0' && *s_ptr <= '9')
12462     {
12463       int gic_page_nr = (*s_ptr++ - '0');
12464
12465       if (*s_ptr >= '0' && *s_ptr <= '9')
12466         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12467
12468       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12469         return ANIM_EVENT_NONE;
12470
12471       // change page stored as 1 to 32 (0 means "all change pages")
12472
12473       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12474     }
12475     else
12476     {
12477       // invalid animation part number specified
12478
12479       return ANIM_EVENT_NONE;
12480     }
12481   }
12482
12483   // discard result if next character is neither delimiter nor whitespace
12484   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12485         *s_ptr == ' ' || *s_ptr == '\t'))
12486     return ANIM_EVENT_NONE;
12487
12488   return result;
12489 }
12490
12491 static int get_anim_parameter_value(char *s)
12492 {
12493   int event_value[] =
12494   {
12495     ANIM_EVENT_CLICK,
12496     ANIM_EVENT_INIT,
12497     ANIM_EVENT_START,
12498     ANIM_EVENT_END,
12499     ANIM_EVENT_POST
12500   };
12501   char *pattern_1[] =
12502   {
12503     "click:anim_",
12504     "init:anim_",
12505     "start:anim_",
12506     "end:anim_",
12507     "post:anim_"
12508   };
12509   char *pattern_2 = ".part_";
12510   char *matching_char = NULL;
12511   char *s_ptr = s;
12512   int pattern_1_len = 0;
12513   int result = ANIM_EVENT_NONE;
12514   int i;
12515
12516   result = get_anim_parameter_value_ce(s);
12517
12518   if (result != ANIM_EVENT_NONE)
12519     return result;
12520
12521   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12522   {
12523     matching_char = strstr(s_ptr, pattern_1[i]);
12524     pattern_1_len = strlen(pattern_1[i]);
12525     result = event_value[i];
12526
12527     if (matching_char != NULL)
12528       break;
12529   }
12530
12531   if (matching_char == NULL)
12532     return ANIM_EVENT_NONE;
12533
12534   s_ptr = matching_char + pattern_1_len;
12535
12536   // check for main animation number ("anim_X" or "anim_XX")
12537   if (*s_ptr >= '0' && *s_ptr <= '9')
12538   {
12539     int gic_anim_nr = (*s_ptr++ - '0');
12540
12541     if (*s_ptr >= '0' && *s_ptr <= '9')
12542       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12543
12544     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12545       return ANIM_EVENT_NONE;
12546
12547     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12548   }
12549   else
12550   {
12551     // invalid main animation number specified
12552
12553     return ANIM_EVENT_NONE;
12554   }
12555
12556   // check for animation part number ("part_X" or "part_XX") (optional)
12557   if (strPrefix(s_ptr, pattern_2))
12558   {
12559     s_ptr += strlen(pattern_2);
12560
12561     if (*s_ptr >= '0' && *s_ptr <= '9')
12562     {
12563       int gic_part_nr = (*s_ptr++ - '0');
12564
12565       if (*s_ptr >= '0' && *s_ptr <= '9')
12566         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12567
12568       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12569         return ANIM_EVENT_NONE;
12570
12571       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12572     }
12573     else
12574     {
12575       // invalid animation part number specified
12576
12577       return ANIM_EVENT_NONE;
12578     }
12579   }
12580
12581   // discard result if next character is neither delimiter nor whitespace
12582   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12583         *s_ptr == ' ' || *s_ptr == '\t'))
12584     return ANIM_EVENT_NONE;
12585
12586   return result;
12587 }
12588
12589 static int get_anim_parameter_values(char *s)
12590 {
12591   int list_pos = ANIM_EVENT_UNDEFINED;
12592   int event_value = ANIM_EVENT_DEFAULT;
12593
12594   if (string_has_parameter(s, "any"))
12595     event_value |= ANIM_EVENT_ANY;
12596
12597   if (string_has_parameter(s, "click:self") ||
12598       string_has_parameter(s, "click") ||
12599       string_has_parameter(s, "self"))
12600     event_value |= ANIM_EVENT_SELF;
12601
12602   if (string_has_parameter(s, "unclick:any"))
12603     event_value |= ANIM_EVENT_UNCLICK_ANY;
12604
12605   // if animation event found, add it to global animation event list
12606   if (event_value != ANIM_EVENT_NONE)
12607     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12608
12609   while (s != NULL)
12610   {
12611     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12612     event_value = get_anim_parameter_value(s);
12613
12614     // if animation event found, add it to global animation event list
12615     if (event_value != ANIM_EVENT_NONE)
12616       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12617
12618     // continue with next part of the string, starting with next comma
12619     s = strchr(s + 1, ',');
12620   }
12621
12622   return list_pos;
12623 }
12624
12625 static int get_anim_action_parameter_value(char *token)
12626 {
12627   // check most common default case first to massively speed things up
12628   if (strEqual(token, ARG_UNDEFINED))
12629     return ANIM_EVENT_ACTION_NONE;
12630
12631   int result = getImageIDFromToken(token);
12632
12633   if (result == -1)
12634   {
12635     char *gfx_token = getStringCat2("gfx.", token);
12636
12637     result = getImageIDFromToken(gfx_token);
12638
12639     checked_free(gfx_token);
12640   }
12641
12642   if (result == -1)
12643   {
12644     Key key = getKeyFromX11KeyName(token);
12645
12646     if (key != KSYM_UNDEFINED)
12647       result = -(int)key;
12648   }
12649
12650   if (result == -1)
12651   {
12652     if (isURL(token))
12653     {
12654       result = get_hash_from_string(token);     // unsigned int => int
12655       result = ABS(result);                     // may be negative now
12656       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12657
12658       setHashEntry(anim_url_hash, int2str(result, 0), token);
12659     }
12660   }
12661
12662   if (result == -1)
12663     result = ANIM_EVENT_ACTION_NONE;
12664
12665   return result;
12666 }
12667
12668 int get_parameter_value(char *value_raw, char *suffix, int type)
12669 {
12670   char *value = getStringToLower(value_raw);
12671   int result = 0;       // probably a save default value
12672
12673   if (strEqual(suffix, ".direction"))
12674   {
12675     result = (strEqual(value, "left")  ? MV_LEFT :
12676               strEqual(value, "right") ? MV_RIGHT :
12677               strEqual(value, "up")    ? MV_UP :
12678               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12679   }
12680   else if (strEqual(suffix, ".position"))
12681   {
12682     result = (strEqual(value, "left")   ? POS_LEFT :
12683               strEqual(value, "right")  ? POS_RIGHT :
12684               strEqual(value, "top")    ? POS_TOP :
12685               strEqual(value, "upper")  ? POS_UPPER :
12686               strEqual(value, "middle") ? POS_MIDDLE :
12687               strEqual(value, "lower")  ? POS_LOWER :
12688               strEqual(value, "bottom") ? POS_BOTTOM :
12689               strEqual(value, "any")    ? POS_ANY :
12690               strEqual(value, "ce")     ? POS_CE :
12691               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12692               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12693   }
12694   else if (strEqual(suffix, ".align"))
12695   {
12696     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12697               strEqual(value, "right")  ? ALIGN_RIGHT :
12698               strEqual(value, "center") ? ALIGN_CENTER :
12699               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12700   }
12701   else if (strEqual(suffix, ".valign"))
12702   {
12703     result = (strEqual(value, "top")    ? VALIGN_TOP :
12704               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12705               strEqual(value, "middle") ? VALIGN_MIDDLE :
12706               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12707   }
12708   else if (strEqual(suffix, ".anim_mode"))
12709   {
12710     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12711               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12712               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12713               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12714               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12715               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12716               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12717               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12718               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12719               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12720               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12721               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12722               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12723               string_has_parameter(value, "all")        ? ANIM_ALL :
12724               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12725               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12726               ANIM_DEFAULT);
12727
12728     if (string_has_parameter(value, "once"))
12729       result |= ANIM_ONCE;
12730
12731     if (string_has_parameter(value, "reverse"))
12732       result |= ANIM_REVERSE;
12733
12734     if (string_has_parameter(value, "opaque_player"))
12735       result |= ANIM_OPAQUE_PLAYER;
12736
12737     if (string_has_parameter(value, "static_panel"))
12738       result |= ANIM_STATIC_PANEL;
12739   }
12740   else if (strEqual(suffix, ".init_event") ||
12741            strEqual(suffix, ".anim_event"))
12742   {
12743     result = get_anim_parameter_values(value);
12744   }
12745   else if (strEqual(suffix, ".init_delay_action") ||
12746            strEqual(suffix, ".anim_delay_action") ||
12747            strEqual(suffix, ".post_delay_action") ||
12748            strEqual(suffix, ".init_event_action") ||
12749            strEqual(suffix, ".anim_event_action"))
12750   {
12751     result = get_anim_action_parameter_value(value_raw);
12752   }
12753   else if (strEqual(suffix, ".class"))
12754   {
12755     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12756               get_hash_from_string(value));
12757   }
12758   else if (strEqual(suffix, ".style"))
12759   {
12760     result = STYLE_DEFAULT;
12761
12762     if (string_has_parameter(value, "accurate_borders"))
12763       result |= STYLE_ACCURATE_BORDERS;
12764
12765     if (string_has_parameter(value, "inner_corners"))
12766       result |= STYLE_INNER_CORNERS;
12767
12768     if (string_has_parameter(value, "reverse"))
12769       result |= STYLE_REVERSE;
12770
12771     if (string_has_parameter(value, "leftmost_position"))
12772       result |= STYLE_LEFTMOST_POSITION;
12773
12774     if (string_has_parameter(value, "block_clicks"))
12775       result |= STYLE_BLOCK;
12776
12777     if (string_has_parameter(value, "passthrough_clicks"))
12778       result |= STYLE_PASSTHROUGH;
12779
12780     if (string_has_parameter(value, "multiple_actions"))
12781       result |= STYLE_MULTIPLE_ACTIONS;
12782
12783     if (string_has_parameter(value, "consume_ce_event"))
12784       result |= STYLE_CONSUME_CE_EVENT;
12785   }
12786   else if (strEqual(suffix, ".fade_mode"))
12787   {
12788     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12789               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12790               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12791               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12792               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12793               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12794               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12795               FADE_MODE_DEFAULT);
12796   }
12797   else if (strEqual(suffix, ".auto_delay_unit"))
12798   {
12799     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12800               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12801               AUTO_DELAY_UNIT_DEFAULT);
12802   }
12803   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12804   {
12805     result = gfx.get_font_from_token_function(value);
12806   }
12807   else          // generic parameter of type integer or boolean
12808   {
12809     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12810               type == TYPE_INTEGER ? get_integer_from_string(value) :
12811               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12812               ARG_UNDEFINED_VALUE);
12813   }
12814
12815   free(value);
12816
12817   return result;
12818 }
12819
12820 static int get_token_parameter_value(char *token, char *value_raw)
12821 {
12822   char *suffix;
12823
12824   if (token == NULL || value_raw == NULL)
12825     return ARG_UNDEFINED_VALUE;
12826
12827   suffix = strrchr(token, '.');
12828   if (suffix == NULL)
12829     suffix = token;
12830
12831   if (strEqual(suffix, ".element"))
12832     return getElementFromToken(value_raw);
12833
12834   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12835   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12836 }
12837
12838 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12839                                      boolean ignore_defaults)
12840 {
12841   int i;
12842
12843   for (i = 0; image_config_vars[i].token != NULL; i++)
12844   {
12845     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12846
12847     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12848     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12849       continue;
12850
12851     if (value != NULL)
12852       *image_config_vars[i].value =
12853         get_token_parameter_value(image_config_vars[i].token, value);
12854   }
12855 }
12856
12857 void InitMenuDesignSettings_Static(void)
12858 {
12859   // always start with reliable default values from static default config
12860   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12861 }
12862
12863 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12864 {
12865   int i;
12866
12867   // the following initializes hierarchical values from static configuration
12868
12869   // special case: initialize "ARG_DEFAULT" values in static default config
12870   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12871   titlescreen_initial_first_default.fade_mode  =
12872     title_initial_first_default.fade_mode;
12873   titlescreen_initial_first_default.fade_delay =
12874     title_initial_first_default.fade_delay;
12875   titlescreen_initial_first_default.post_delay =
12876     title_initial_first_default.post_delay;
12877   titlescreen_initial_first_default.auto_delay =
12878     title_initial_first_default.auto_delay;
12879   titlescreen_initial_first_default.auto_delay_unit =
12880     title_initial_first_default.auto_delay_unit;
12881   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12882   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12883   titlescreen_first_default.post_delay = title_first_default.post_delay;
12884   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12885   titlescreen_first_default.auto_delay_unit =
12886     title_first_default.auto_delay_unit;
12887   titlemessage_initial_first_default.fade_mode  =
12888     title_initial_first_default.fade_mode;
12889   titlemessage_initial_first_default.fade_delay =
12890     title_initial_first_default.fade_delay;
12891   titlemessage_initial_first_default.post_delay =
12892     title_initial_first_default.post_delay;
12893   titlemessage_initial_first_default.auto_delay =
12894     title_initial_first_default.auto_delay;
12895   titlemessage_initial_first_default.auto_delay_unit =
12896     title_initial_first_default.auto_delay_unit;
12897   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12898   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12899   titlemessage_first_default.post_delay = title_first_default.post_delay;
12900   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12901   titlemessage_first_default.auto_delay_unit =
12902     title_first_default.auto_delay_unit;
12903
12904   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12905   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12906   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12907   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12908   titlescreen_initial_default.auto_delay_unit =
12909     title_initial_default.auto_delay_unit;
12910   titlescreen_default.fade_mode  = title_default.fade_mode;
12911   titlescreen_default.fade_delay = title_default.fade_delay;
12912   titlescreen_default.post_delay = title_default.post_delay;
12913   titlescreen_default.auto_delay = title_default.auto_delay;
12914   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12915   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12916   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12917   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12918   titlemessage_initial_default.auto_delay_unit =
12919     title_initial_default.auto_delay_unit;
12920   titlemessage_default.fade_mode  = title_default.fade_mode;
12921   titlemessage_default.fade_delay = title_default.fade_delay;
12922   titlemessage_default.post_delay = title_default.post_delay;
12923   titlemessage_default.auto_delay = title_default.auto_delay;
12924   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12925
12926   // special case: initialize "ARG_DEFAULT" values in static default config
12927   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12928   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12929   {
12930     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12931     titlescreen_first[i] = titlescreen_first_default;
12932     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12933     titlemessage_first[i] = titlemessage_first_default;
12934
12935     titlescreen_initial[i] = titlescreen_initial_default;
12936     titlescreen[i] = titlescreen_default;
12937     titlemessage_initial[i] = titlemessage_initial_default;
12938     titlemessage[i] = titlemessage_default;
12939   }
12940
12941   // special case: initialize "ARG_DEFAULT" values in static default config
12942   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12943   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12944   {
12945     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12946       continue;
12947
12948     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12949     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12950     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12951   }
12952
12953   // special case: initialize "ARG_DEFAULT" values in static default config
12954   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12955   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12956   {
12957     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12958     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12959     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12960
12961     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12962       continue;
12963
12964     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12965   }
12966 }
12967
12968 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12969 {
12970   static struct
12971   {
12972     struct XY *dst, *src;
12973   }
12974   game_buttons_xy[] =
12975   {
12976     { &game.button.save,        &game.button.stop       },
12977     { &game.button.pause2,      &game.button.pause      },
12978     { &game.button.load,        &game.button.play       },
12979     { &game.button.undo,        &game.button.stop       },
12980     { &game.button.redo,        &game.button.play       },
12981
12982     { NULL,                     NULL                    }
12983   };
12984   int i, j;
12985
12986   // special case: initialize later added SETUP list size from LEVELS value
12987   if (menu.list_size[GAME_MODE_SETUP] == -1)
12988     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12989
12990   // set default position for snapshot buttons to stop/pause/play buttons
12991   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12992     if ((*game_buttons_xy[i].dst).x == -1 &&
12993         (*game_buttons_xy[i].dst).y == -1)
12994       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12995
12996   // --------------------------------------------------------------------------
12997   // dynamic viewports (including playfield margins, borders and alignments)
12998   // --------------------------------------------------------------------------
12999
13000   // dynamic viewports currently only supported for landscape mode
13001   int display_width  = MAX(video.display_width, video.display_height);
13002   int display_height = MIN(video.display_width, video.display_height);
13003
13004   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13005   {
13006     struct RectWithBorder *vp_window    = &viewport.window[i];
13007     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13008     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13009     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13010     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13011     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13012     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13013     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13014
13015     // adjust window size if min/max width/height is specified
13016
13017     if (vp_window->min_width != -1)
13018     {
13019       int window_width = display_width;
13020
13021       // when using static window height, use aspect ratio of display
13022       if (vp_window->min_height == -1)
13023         window_width = vp_window->height * display_width / display_height;
13024
13025       vp_window->width = MAX(vp_window->min_width, window_width);
13026     }
13027
13028     if (vp_window->min_height != -1)
13029     {
13030       int window_height = display_height;
13031
13032       // when using static window width, use aspect ratio of display
13033       if (vp_window->min_width == -1)
13034         window_height = vp_window->width * display_height / display_width;
13035
13036       vp_window->height = MAX(vp_window->min_height, window_height);
13037     }
13038
13039     if (vp_window->max_width != -1)
13040       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13041
13042     if (vp_window->max_height != -1)
13043       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13044
13045     int playfield_width  = vp_window->width;
13046     int playfield_height = vp_window->height;
13047
13048     // adjust playfield size and position according to specified margins
13049
13050     playfield_width  -= vp_playfield->margin_left;
13051     playfield_width  -= vp_playfield->margin_right;
13052
13053     playfield_height -= vp_playfield->margin_top;
13054     playfield_height -= vp_playfield->margin_bottom;
13055
13056     // adjust playfield size if min/max width/height is specified
13057
13058     if (vp_playfield->min_width != -1)
13059       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13060
13061     if (vp_playfield->min_height != -1)
13062       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13063
13064     if (vp_playfield->max_width != -1)
13065       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13066
13067     if (vp_playfield->max_height != -1)
13068       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13069
13070     // adjust playfield position according to specified alignment
13071
13072     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13073       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13074     else if (vp_playfield->align == ALIGN_CENTER)
13075       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13076     else if (vp_playfield->align == ALIGN_RIGHT)
13077       vp_playfield->x += playfield_width - vp_playfield->width;
13078
13079     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13080       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13081     else if (vp_playfield->valign == VALIGN_MIDDLE)
13082       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13083     else if (vp_playfield->valign == VALIGN_BOTTOM)
13084       vp_playfield->y += playfield_height - vp_playfield->height;
13085
13086     vp_playfield->x += vp_playfield->margin_left;
13087     vp_playfield->y += vp_playfield->margin_top;
13088
13089     // adjust individual playfield borders if only default border is specified
13090
13091     if (vp_playfield->border_left == -1)
13092       vp_playfield->border_left = vp_playfield->border_size;
13093     if (vp_playfield->border_right == -1)
13094       vp_playfield->border_right = vp_playfield->border_size;
13095     if (vp_playfield->border_top == -1)
13096       vp_playfield->border_top = vp_playfield->border_size;
13097     if (vp_playfield->border_bottom == -1)
13098       vp_playfield->border_bottom = vp_playfield->border_size;
13099
13100     // set dynamic playfield borders if borders are specified as undefined
13101     // (but only if window size was dynamic and playfield size was static)
13102
13103     if (dynamic_window_width && !dynamic_playfield_width)
13104     {
13105       if (vp_playfield->border_left == -1)
13106       {
13107         vp_playfield->border_left = (vp_playfield->x -
13108                                      vp_playfield->margin_left);
13109         vp_playfield->x     -= vp_playfield->border_left;
13110         vp_playfield->width += vp_playfield->border_left;
13111       }
13112
13113       if (vp_playfield->border_right == -1)
13114       {
13115         vp_playfield->border_right = (vp_window->width -
13116                                       vp_playfield->x -
13117                                       vp_playfield->width -
13118                                       vp_playfield->margin_right);
13119         vp_playfield->width += vp_playfield->border_right;
13120       }
13121     }
13122
13123     if (dynamic_window_height && !dynamic_playfield_height)
13124     {
13125       if (vp_playfield->border_top == -1)
13126       {
13127         vp_playfield->border_top = (vp_playfield->y -
13128                                     vp_playfield->margin_top);
13129         vp_playfield->y      -= vp_playfield->border_top;
13130         vp_playfield->height += vp_playfield->border_top;
13131       }
13132
13133       if (vp_playfield->border_bottom == -1)
13134       {
13135         vp_playfield->border_bottom = (vp_window->height -
13136                                        vp_playfield->y -
13137                                        vp_playfield->height -
13138                                        vp_playfield->margin_bottom);
13139         vp_playfield->height += vp_playfield->border_bottom;
13140       }
13141     }
13142
13143     // adjust playfield size to be a multiple of a defined alignment tile size
13144
13145     int align_size = vp_playfield->align_size;
13146     int playfield_xtiles = vp_playfield->width  / align_size;
13147     int playfield_ytiles = vp_playfield->height / align_size;
13148     int playfield_width_corrected  = playfield_xtiles * align_size;
13149     int playfield_height_corrected = playfield_ytiles * align_size;
13150     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13151                                  i == GFX_SPECIAL_ARG_EDITOR);
13152
13153     if (is_playfield_mode &&
13154         dynamic_playfield_width &&
13155         vp_playfield->width != playfield_width_corrected)
13156     {
13157       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13158
13159       vp_playfield->width = playfield_width_corrected;
13160
13161       if (vp_playfield->align == ALIGN_LEFT)
13162       {
13163         vp_playfield->border_left += playfield_xdiff;
13164       }
13165       else if (vp_playfield->align == ALIGN_RIGHT)
13166       {
13167         vp_playfield->border_right += playfield_xdiff;
13168       }
13169       else if (vp_playfield->align == ALIGN_CENTER)
13170       {
13171         int border_left_diff  = playfield_xdiff / 2;
13172         int border_right_diff = playfield_xdiff - border_left_diff;
13173
13174         vp_playfield->border_left  += border_left_diff;
13175         vp_playfield->border_right += border_right_diff;
13176       }
13177     }
13178
13179     if (is_playfield_mode &&
13180         dynamic_playfield_height &&
13181         vp_playfield->height != playfield_height_corrected)
13182     {
13183       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13184
13185       vp_playfield->height = playfield_height_corrected;
13186
13187       if (vp_playfield->valign == VALIGN_TOP)
13188       {
13189         vp_playfield->border_top += playfield_ydiff;
13190       }
13191       else if (vp_playfield->align == VALIGN_BOTTOM)
13192       {
13193         vp_playfield->border_right += playfield_ydiff;
13194       }
13195       else if (vp_playfield->align == VALIGN_MIDDLE)
13196       {
13197         int border_top_diff    = playfield_ydiff / 2;
13198         int border_bottom_diff = playfield_ydiff - border_top_diff;
13199
13200         vp_playfield->border_top    += border_top_diff;
13201         vp_playfield->border_bottom += border_bottom_diff;
13202       }
13203     }
13204
13205     // adjust door positions according to specified alignment
13206
13207     for (j = 0; j < 2; j++)
13208     {
13209       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13210
13211       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13212         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13213       else if (vp_door->align == ALIGN_CENTER)
13214         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13215       else if (vp_door->align == ALIGN_RIGHT)
13216         vp_door->x += vp_window->width - vp_door->width;
13217
13218       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13219         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13220       else if (vp_door->valign == VALIGN_MIDDLE)
13221         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13222       else if (vp_door->valign == VALIGN_BOTTOM)
13223         vp_door->y += vp_window->height - vp_door->height;
13224     }
13225   }
13226 }
13227
13228 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13229 {
13230   static struct
13231   {
13232     struct XYTileSize *dst, *src;
13233     int graphic;
13234   }
13235   editor_buttons_xy[] =
13236   {
13237     {
13238       &editor.button.element_left,      &editor.palette.element_left,
13239       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13240     },
13241     {
13242       &editor.button.element_middle,    &editor.palette.element_middle,
13243       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13244     },
13245     {
13246       &editor.button.element_right,     &editor.palette.element_right,
13247       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13248     },
13249
13250     { NULL,                     NULL                    }
13251   };
13252   int i;
13253
13254   // set default position for element buttons to element graphics
13255   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13256   {
13257     if ((*editor_buttons_xy[i].dst).x == -1 &&
13258         (*editor_buttons_xy[i].dst).y == -1)
13259     {
13260       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13261
13262       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13263
13264       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13265     }
13266   }
13267
13268   // adjust editor palette rows and columns if specified to be dynamic
13269
13270   if (editor.palette.cols == -1)
13271   {
13272     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13273     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13274     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13275
13276     editor.palette.cols = (vp_width - sc_width) / bt_width;
13277
13278     if (editor.palette.x == -1)
13279     {
13280       int palette_width = editor.palette.cols * bt_width + sc_width;
13281
13282       editor.palette.x = (vp_width - palette_width) / 2;
13283     }
13284   }
13285
13286   if (editor.palette.rows == -1)
13287   {
13288     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13289     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13290     int tx_height = getFontHeight(FONT_TEXT_2);
13291
13292     editor.palette.rows = (vp_height - tx_height) / bt_height;
13293
13294     if (editor.palette.y == -1)
13295     {
13296       int palette_height = editor.palette.rows * bt_height + tx_height;
13297
13298       editor.palette.y = (vp_height - palette_height) / 2;
13299     }
13300   }
13301 }
13302
13303 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13304                                                       boolean initialize)
13305 {
13306   // special case: check if network and preview player positions are redefined,
13307   // to compare this later against the main menu level preview being redefined
13308   struct TokenIntPtrInfo menu_config_players[] =
13309   {
13310     { "main.network_players.x", &menu.main.network_players.redefined    },
13311     { "main.network_players.y", &menu.main.network_players.redefined    },
13312     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13313     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13314     { "preview.x",              &preview.redefined                      },
13315     { "preview.y",              &preview.redefined                      }
13316   };
13317   int i;
13318
13319   if (initialize)
13320   {
13321     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13322       *menu_config_players[i].value = FALSE;
13323   }
13324   else
13325   {
13326     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13327       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13328         *menu_config_players[i].value = TRUE;
13329   }
13330 }
13331
13332 static void InitMenuDesignSettings_PreviewPlayers(void)
13333 {
13334   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13335 }
13336
13337 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13338 {
13339   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13340 }
13341
13342 static void LoadMenuDesignSettingsFromFilename(char *filename)
13343 {
13344   static struct TitleFadingInfo tfi;
13345   static struct TitleMessageInfo tmi;
13346   static struct TokenInfo title_tokens[] =
13347   {
13348     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13349     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13350     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13351     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13352     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13353
13354     { -1,               NULL,                   NULL                    }
13355   };
13356   static struct TokenInfo titlemessage_tokens[] =
13357   {
13358     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13359     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13360     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13361     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13362     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13363     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13364     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13365     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13366     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13367     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13368     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13369     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13370     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13371     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13372     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13373     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13374     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13375     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13376
13377     { -1,               NULL,                   NULL                    }
13378   };
13379   static struct
13380   {
13381     struct TitleFadingInfo *info;
13382     char *text;
13383   }
13384   title_info[] =
13385   {
13386     // initialize first titles from "enter screen" definitions, if defined
13387     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13388     { &title_first_default,             "menu.enter_screen.TITLE"       },
13389
13390     // initialize title screens from "next screen" definitions, if defined
13391     { &title_initial_default,           "menu.next_screen.TITLE"        },
13392     { &title_default,                   "menu.next_screen.TITLE"        },
13393
13394     { NULL,                             NULL                            }
13395   };
13396   static struct
13397   {
13398     struct TitleMessageInfo *array;
13399     char *text;
13400   }
13401   titlemessage_arrays[] =
13402   {
13403     // initialize first titles from "enter screen" definitions, if defined
13404     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13405     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13406     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13407     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13408
13409     // initialize titles from "next screen" definitions, if defined
13410     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13411     { titlescreen,                      "menu.next_screen.TITLE"        },
13412     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13413     { titlemessage,                     "menu.next_screen.TITLE"        },
13414
13415     // overwrite titles with title definitions, if defined
13416     { titlescreen_initial_first,        "[title_initial]"               },
13417     { titlescreen_first,                "[title]"                       },
13418     { titlemessage_initial_first,       "[title_initial]"               },
13419     { titlemessage_first,               "[title]"                       },
13420
13421     { titlescreen_initial,              "[title_initial]"               },
13422     { titlescreen,                      "[title]"                       },
13423     { titlemessage_initial,             "[title_initial]"               },
13424     { titlemessage,                     "[title]"                       },
13425
13426     // overwrite titles with title screen/message definitions, if defined
13427     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13428     { titlescreen_first,                "[titlescreen]"                 },
13429     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13430     { titlemessage_first,               "[titlemessage]"                },
13431
13432     { titlescreen_initial,              "[titlescreen_initial]"         },
13433     { titlescreen,                      "[titlescreen]"                 },
13434     { titlemessage_initial,             "[titlemessage_initial]"        },
13435     { titlemessage,                     "[titlemessage]"                },
13436
13437     { NULL,                             NULL                            }
13438   };
13439   SetupFileHash *setup_file_hash;
13440   int i, j, k;
13441
13442   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13443     return;
13444
13445   // the following initializes hierarchical values from dynamic configuration
13446
13447   // special case: initialize with default values that may be overwritten
13448   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13449   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13450   {
13451     struct TokenIntPtrInfo menu_config[] =
13452     {
13453       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13454       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13455       { "menu.list_size",       &menu.list_size[i]      }
13456     };
13457
13458     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13459     {
13460       char *token = menu_config[j].token;
13461       char *value = getHashEntry(setup_file_hash, token);
13462
13463       if (value != NULL)
13464         *menu_config[j].value = get_integer_from_string(value);
13465     }
13466   }
13467
13468   // special case: initialize with default values that may be overwritten
13469   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13470   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13471   {
13472     struct TokenIntPtrInfo menu_config[] =
13473     {
13474       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13475       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13476       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13477       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13478       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13479     };
13480
13481     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13482     {
13483       char *token = menu_config[j].token;
13484       char *value = getHashEntry(setup_file_hash, token);
13485
13486       if (value != NULL)
13487         *menu_config[j].value = get_integer_from_string(value);
13488     }
13489   }
13490
13491   // special case: initialize with default values that may be overwritten
13492   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13493   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13494   {
13495     struct TokenIntPtrInfo menu_config[] =
13496     {
13497       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13498       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13499     };
13500
13501     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13502     {
13503       char *token = menu_config[j].token;
13504       char *value = getHashEntry(setup_file_hash, token);
13505
13506       if (value != NULL)
13507         *menu_config[j].value = get_integer_from_string(value);
13508     }
13509   }
13510
13511   // special case: initialize with default values that may be overwritten
13512   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13513   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13514   {
13515     struct TokenIntPtrInfo menu_config[] =
13516     {
13517       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13518       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13519       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13520       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13521       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13522       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13523       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13524       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13525       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13526       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13527     };
13528
13529     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13530     {
13531       char *token = menu_config[j].token;
13532       char *value = getHashEntry(setup_file_hash, token);
13533
13534       if (value != NULL)
13535         *menu_config[j].value = get_integer_from_string(value);
13536     }
13537   }
13538
13539   // special case: initialize with default values that may be overwritten
13540   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13541   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13542   {
13543     struct TokenIntPtrInfo menu_config[] =
13544     {
13545       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13546       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13547       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13548       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13549       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13550       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13551       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13552       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13553       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13554     };
13555
13556     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13557     {
13558       char *token = menu_config[j].token;
13559       char *value = getHashEntry(setup_file_hash, token);
13560
13561       if (value != NULL)
13562         *menu_config[j].value = get_token_parameter_value(token, value);
13563     }
13564   }
13565
13566   // special case: initialize with default values that may be overwritten
13567   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13568   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13569   {
13570     struct
13571     {
13572       char *token_prefix;
13573       struct RectWithBorder *struct_ptr;
13574     }
13575     vp_struct[] =
13576     {
13577       { "viewport.window",      &viewport.window[i]     },
13578       { "viewport.playfield",   &viewport.playfield[i]  },
13579       { "viewport.door_1",      &viewport.door_1[i]     },
13580       { "viewport.door_2",      &viewport.door_2[i]     }
13581     };
13582
13583     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13584     {
13585       struct TokenIntPtrInfo vp_config[] =
13586       {
13587         { ".x",                 &vp_struct[j].struct_ptr->x             },
13588         { ".y",                 &vp_struct[j].struct_ptr->y             },
13589         { ".width",             &vp_struct[j].struct_ptr->width         },
13590         { ".height",            &vp_struct[j].struct_ptr->height        },
13591         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13592         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13593         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13594         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13595         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13596         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13597         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13598         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13599         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13600         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13601         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13602         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13603         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13604         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13605         { ".align",             &vp_struct[j].struct_ptr->align         },
13606         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13607       };
13608
13609       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13610       {
13611         char *token = getStringCat2(vp_struct[j].token_prefix,
13612                                     vp_config[k].token);
13613         char *value = getHashEntry(setup_file_hash, token);
13614
13615         if (value != NULL)
13616           *vp_config[k].value = get_token_parameter_value(token, value);
13617
13618         free(token);
13619       }
13620     }
13621   }
13622
13623   // special case: initialize with default values that may be overwritten
13624   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13625   for (i = 0; title_info[i].info != NULL; i++)
13626   {
13627     struct TitleFadingInfo *info = title_info[i].info;
13628     char *base_token = title_info[i].text;
13629
13630     for (j = 0; title_tokens[j].type != -1; j++)
13631     {
13632       char *token = getStringCat2(base_token, title_tokens[j].text);
13633       char *value = getHashEntry(setup_file_hash, token);
13634
13635       if (value != NULL)
13636       {
13637         int parameter_value = get_token_parameter_value(token, value);
13638
13639         tfi = *info;
13640
13641         *(int *)title_tokens[j].value = (int)parameter_value;
13642
13643         *info = tfi;
13644       }
13645
13646       free(token);
13647     }
13648   }
13649
13650   // special case: initialize with default values that may be overwritten
13651   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13652   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13653   {
13654     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13655     char *base_token = titlemessage_arrays[i].text;
13656
13657     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13658     {
13659       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13660       char *value = getHashEntry(setup_file_hash, token);
13661
13662       if (value != NULL)
13663       {
13664         int parameter_value = get_token_parameter_value(token, value);
13665
13666         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13667         {
13668           tmi = array[k];
13669
13670           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13671             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13672           else
13673             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13674
13675           array[k] = tmi;
13676         }
13677       }
13678
13679       free(token);
13680     }
13681   }
13682
13683   // read (and overwrite with) values that may be specified in config file
13684   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13685
13686   // special case: check if network and preview player positions are redefined
13687   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13688
13689   freeSetupFileHash(setup_file_hash);
13690 }
13691
13692 void LoadMenuDesignSettings(void)
13693 {
13694   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13695
13696   InitMenuDesignSettings_Static();
13697   InitMenuDesignSettings_SpecialPreProcessing();
13698   InitMenuDesignSettings_PreviewPlayers();
13699
13700   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13701   {
13702     // first look for special settings configured in level series config
13703     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13704
13705     if (fileExists(filename_base))
13706       LoadMenuDesignSettingsFromFilename(filename_base);
13707   }
13708
13709   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13710
13711   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13712     LoadMenuDesignSettingsFromFilename(filename_local);
13713
13714   InitMenuDesignSettings_SpecialPostProcessing();
13715 }
13716
13717 void LoadMenuDesignSettings_AfterGraphics(void)
13718 {
13719   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13720 }
13721
13722 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13723                                 boolean ignore_defaults)
13724 {
13725   int i;
13726
13727   for (i = 0; sound_config_vars[i].token != NULL; i++)
13728   {
13729     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13730
13731     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13732     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13733       continue;
13734
13735     if (value != NULL)
13736       *sound_config_vars[i].value =
13737         get_token_parameter_value(sound_config_vars[i].token, value);
13738   }
13739 }
13740
13741 void InitSoundSettings_Static(void)
13742 {
13743   // always start with reliable default values from static default config
13744   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13745 }
13746
13747 static void LoadSoundSettingsFromFilename(char *filename)
13748 {
13749   SetupFileHash *setup_file_hash;
13750
13751   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13752     return;
13753
13754   // read (and overwrite with) values that may be specified in config file
13755   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13756
13757   freeSetupFileHash(setup_file_hash);
13758 }
13759
13760 void LoadSoundSettings(void)
13761 {
13762   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13763
13764   InitSoundSettings_Static();
13765
13766   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13767   {
13768     // first look for special settings configured in level series config
13769     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13770
13771     if (fileExists(filename_base))
13772       LoadSoundSettingsFromFilename(filename_base);
13773   }
13774
13775   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13776
13777   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13778     LoadSoundSettingsFromFilename(filename_local);
13779 }
13780
13781 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13782 {
13783   char *filename = getEditorSetupFilename();
13784   SetupFileList *setup_file_list, *list;
13785   SetupFileHash *element_hash;
13786   int num_unknown_tokens = 0;
13787   int i;
13788
13789   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13790     return;
13791
13792   element_hash = newSetupFileHash();
13793
13794   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13795     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13796
13797   // determined size may be larger than needed (due to unknown elements)
13798   *num_elements = 0;
13799   for (list = setup_file_list; list != NULL; list = list->next)
13800     (*num_elements)++;
13801
13802   // add space for up to 3 more elements for padding that may be needed
13803   *num_elements += 3;
13804
13805   // free memory for old list of elements, if needed
13806   checked_free(*elements);
13807
13808   // allocate memory for new list of elements
13809   *elements = checked_malloc(*num_elements * sizeof(int));
13810
13811   *num_elements = 0;
13812   for (list = setup_file_list; list != NULL; list = list->next)
13813   {
13814     char *value = getHashEntry(element_hash, list->token);
13815
13816     if (value == NULL)          // try to find obsolete token mapping
13817     {
13818       char *mapped_token = get_mapped_token(list->token);
13819
13820       if (mapped_token != NULL)
13821       {
13822         value = getHashEntry(element_hash, mapped_token);
13823
13824         free(mapped_token);
13825       }
13826     }
13827
13828     if (value != NULL)
13829     {
13830       (*elements)[(*num_elements)++] = atoi(value);
13831     }
13832     else
13833     {
13834       if (num_unknown_tokens == 0)
13835       {
13836         Warn("---");
13837         Warn("unknown token(s) found in config file:");
13838         Warn("- config file: '%s'", filename);
13839
13840         num_unknown_tokens++;
13841       }
13842
13843       Warn("- token: '%s'", list->token);
13844     }
13845   }
13846
13847   if (num_unknown_tokens > 0)
13848     Warn("---");
13849
13850   while (*num_elements % 4)     // pad with empty elements, if needed
13851     (*elements)[(*num_elements)++] = EL_EMPTY;
13852
13853   freeSetupFileList(setup_file_list);
13854   freeSetupFileHash(element_hash);
13855
13856 #if 0
13857   for (i = 0; i < *num_elements; i++)
13858     Debug("editor", "element '%s' [%d]\n",
13859           element_info[(*elements)[i]].token_name, (*elements)[i]);
13860 #endif
13861 }
13862
13863 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13864                                                      boolean is_sound)
13865 {
13866   SetupFileHash *setup_file_hash = NULL;
13867   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13868   char *filename_music, *filename_prefix, *filename_info;
13869   struct
13870   {
13871     char *token;
13872     char **value_ptr;
13873   }
13874   token_to_value_ptr[] =
13875   {
13876     { "title_header",   &tmp_music_file_info.title_header       },
13877     { "artist_header",  &tmp_music_file_info.artist_header      },
13878     { "album_header",   &tmp_music_file_info.album_header       },
13879     { "year_header",    &tmp_music_file_info.year_header        },
13880     { "played_header",  &tmp_music_file_info.played_header      },
13881
13882     { "title",          &tmp_music_file_info.title              },
13883     { "artist",         &tmp_music_file_info.artist             },
13884     { "album",          &tmp_music_file_info.album              },
13885     { "year",           &tmp_music_file_info.year               },
13886     { "played",         &tmp_music_file_info.played             },
13887
13888     { NULL,             NULL                                    },
13889   };
13890   int i;
13891
13892   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13893                     getCustomMusicFilename(basename));
13894
13895   if (filename_music == NULL)
13896     return NULL;
13897
13898   // ---------- try to replace file extension ----------
13899
13900   filename_prefix = getStringCopy(filename_music);
13901   if (strrchr(filename_prefix, '.') != NULL)
13902     *strrchr(filename_prefix, '.') = '\0';
13903   filename_info = getStringCat2(filename_prefix, ".txt");
13904
13905   if (fileExists(filename_info))
13906     setup_file_hash = loadSetupFileHash(filename_info);
13907
13908   free(filename_prefix);
13909   free(filename_info);
13910
13911   if (setup_file_hash == NULL)
13912   {
13913     // ---------- try to add file extension ----------
13914
13915     filename_prefix = getStringCopy(filename_music);
13916     filename_info = getStringCat2(filename_prefix, ".txt");
13917
13918     if (fileExists(filename_info))
13919       setup_file_hash = loadSetupFileHash(filename_info);
13920
13921     free(filename_prefix);
13922     free(filename_info);
13923   }
13924
13925   if (setup_file_hash == NULL)
13926     return NULL;
13927
13928   // ---------- music file info found ----------
13929
13930   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13931
13932   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13933   {
13934     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13935
13936     *token_to_value_ptr[i].value_ptr =
13937       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13938   }
13939
13940   tmp_music_file_info.basename = getStringCopy(basename);
13941   tmp_music_file_info.music = music;
13942   tmp_music_file_info.is_sound = is_sound;
13943
13944   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13945   *new_music_file_info = tmp_music_file_info;
13946
13947   return new_music_file_info;
13948 }
13949
13950 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13951 {
13952   return get_music_file_info_ext(basename, music, FALSE);
13953 }
13954
13955 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13956 {
13957   return get_music_file_info_ext(basename, sound, TRUE);
13958 }
13959
13960 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13961                                      char *basename, boolean is_sound)
13962 {
13963   for (; list != NULL; list = list->next)
13964     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13965       return TRUE;
13966
13967   return FALSE;
13968 }
13969
13970 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13971 {
13972   return music_info_listed_ext(list, basename, FALSE);
13973 }
13974
13975 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13976 {
13977   return music_info_listed_ext(list, basename, TRUE);
13978 }
13979
13980 void LoadMusicInfo(void)
13981 {
13982   int num_music_noconf = getMusicListSize_NoConf();
13983   int num_music = getMusicListSize();
13984   int num_sounds = getSoundListSize();
13985   struct FileInfo *music, *sound;
13986   struct MusicFileInfo *next, **new;
13987
13988   int i;
13989
13990   while (music_file_info != NULL)
13991   {
13992     next = music_file_info->next;
13993
13994     checked_free(music_file_info->basename);
13995
13996     checked_free(music_file_info->title_header);
13997     checked_free(music_file_info->artist_header);
13998     checked_free(music_file_info->album_header);
13999     checked_free(music_file_info->year_header);
14000     checked_free(music_file_info->played_header);
14001
14002     checked_free(music_file_info->title);
14003     checked_free(music_file_info->artist);
14004     checked_free(music_file_info->album);
14005     checked_free(music_file_info->year);
14006     checked_free(music_file_info->played);
14007
14008     free(music_file_info);
14009
14010     music_file_info = next;
14011   }
14012
14013   new = &music_file_info;
14014
14015   // get (configured or unconfigured) music file info for all levels
14016   for (i = leveldir_current->first_level;
14017        i <= leveldir_current->last_level; i++)
14018   {
14019     int music_nr;
14020
14021     if (levelset.music[i] != MUS_UNDEFINED)
14022     {
14023       // get music file info for configured level music
14024       music_nr = levelset.music[i];
14025     }
14026     else if (num_music_noconf > 0)
14027     {
14028       // get music file info for unconfigured level music
14029       int level_pos = i - leveldir_current->first_level;
14030
14031       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14032     }
14033     else
14034     {
14035       continue;
14036     }
14037
14038     char *basename = getMusicInfoEntryFilename(music_nr);
14039
14040     if (basename == NULL)
14041       continue;
14042
14043     if (!music_info_listed(music_file_info, basename))
14044     {
14045       *new = get_music_file_info(basename, music_nr);
14046
14047       if (*new != NULL)
14048         new = &(*new)->next;
14049     }
14050   }
14051
14052   // get music file info for all remaining configured music files
14053   for (i = 0; i < num_music; i++)
14054   {
14055     music = getMusicListEntry(i);
14056
14057     if (music->filename == NULL)
14058       continue;
14059
14060     if (strEqual(music->filename, UNDEFINED_FILENAME))
14061       continue;
14062
14063     // a configured file may be not recognized as music
14064     if (!FileIsMusic(music->filename))
14065       continue;
14066
14067     if (!music_info_listed(music_file_info, music->filename))
14068     {
14069       *new = get_music_file_info(music->filename, i);
14070
14071       if (*new != NULL)
14072         new = &(*new)->next;
14073     }
14074   }
14075
14076   // get sound file info for all configured sound files
14077   for (i = 0; i < num_sounds; i++)
14078   {
14079     sound = getSoundListEntry(i);
14080
14081     if (sound->filename == NULL)
14082       continue;
14083
14084     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14085       continue;
14086
14087     // a configured file may be not recognized as sound
14088     if (!FileIsSound(sound->filename))
14089       continue;
14090
14091     if (!sound_info_listed(music_file_info, sound->filename))
14092     {
14093       *new = get_sound_file_info(sound->filename, i);
14094       if (*new != NULL)
14095         new = &(*new)->next;
14096     }
14097   }
14098
14099   // add pointers to previous list nodes
14100
14101   struct MusicFileInfo *node = music_file_info;
14102
14103   while (node != NULL)
14104   {
14105     if (node->next)
14106       node->next->prev = node;
14107
14108     node = node->next;
14109   }
14110 }
14111
14112 static void add_helpanim_entry(int element, int action, int direction,
14113                                int delay, int *num_list_entries)
14114 {
14115   struct HelpAnimInfo *new_list_entry;
14116   (*num_list_entries)++;
14117
14118   helpanim_info =
14119     checked_realloc(helpanim_info,
14120                     *num_list_entries * sizeof(struct HelpAnimInfo));
14121   new_list_entry = &helpanim_info[*num_list_entries - 1];
14122
14123   new_list_entry->element = element;
14124   new_list_entry->action = action;
14125   new_list_entry->direction = direction;
14126   new_list_entry->delay = delay;
14127 }
14128
14129 static void print_unknown_token(char *filename, char *token, int token_nr)
14130 {
14131   if (token_nr == 0)
14132   {
14133     Warn("---");
14134     Warn("unknown token(s) found in config file:");
14135     Warn("- config file: '%s'", filename);
14136   }
14137
14138   Warn("- token: '%s'", token);
14139 }
14140
14141 static void print_unknown_token_end(int token_nr)
14142 {
14143   if (token_nr > 0)
14144     Warn("---");
14145 }
14146
14147 void LoadHelpAnimInfo(void)
14148 {
14149   char *filename = getHelpAnimFilename();
14150   SetupFileList *setup_file_list = NULL, *list;
14151   SetupFileHash *element_hash, *action_hash, *direction_hash;
14152   int num_list_entries = 0;
14153   int num_unknown_tokens = 0;
14154   int i;
14155
14156   if (fileExists(filename))
14157     setup_file_list = loadSetupFileList(filename);
14158
14159   if (setup_file_list == NULL)
14160   {
14161     // use reliable default values from static configuration
14162     SetupFileList *insert_ptr;
14163
14164     insert_ptr = setup_file_list =
14165       newSetupFileList(helpanim_config[0].token,
14166                        helpanim_config[0].value);
14167
14168     for (i = 1; helpanim_config[i].token; i++)
14169       insert_ptr = addListEntry(insert_ptr,
14170                                 helpanim_config[i].token,
14171                                 helpanim_config[i].value);
14172   }
14173
14174   element_hash   = newSetupFileHash();
14175   action_hash    = newSetupFileHash();
14176   direction_hash = newSetupFileHash();
14177
14178   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14179     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14180
14181   for (i = 0; i < NUM_ACTIONS; i++)
14182     setHashEntry(action_hash, element_action_info[i].suffix,
14183                  i_to_a(element_action_info[i].value));
14184
14185   // do not store direction index (bit) here, but direction value!
14186   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14187     setHashEntry(direction_hash, element_direction_info[i].suffix,
14188                  i_to_a(1 << element_direction_info[i].value));
14189
14190   for (list = setup_file_list; list != NULL; list = list->next)
14191   {
14192     char *element_token, *action_token, *direction_token;
14193     char *element_value, *action_value, *direction_value;
14194     int delay = atoi(list->value);
14195
14196     if (strEqual(list->token, "end"))
14197     {
14198       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14199
14200       continue;
14201     }
14202
14203     /* first try to break element into element/action/direction parts;
14204        if this does not work, also accept combined "element[.act][.dir]"
14205        elements (like "dynamite.active"), which are unique elements */
14206
14207     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14208     {
14209       element_value = getHashEntry(element_hash, list->token);
14210       if (element_value != NULL)        // element found
14211         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14212                            &num_list_entries);
14213       else
14214       {
14215         // no further suffixes found -- this is not an element
14216         print_unknown_token(filename, list->token, num_unknown_tokens++);
14217       }
14218
14219       continue;
14220     }
14221
14222     // token has format "<prefix>.<something>"
14223
14224     action_token = strchr(list->token, '.');    // suffix may be action ...
14225     direction_token = action_token;             // ... or direction
14226
14227     element_token = getStringCopy(list->token);
14228     *strchr(element_token, '.') = '\0';
14229
14230     element_value = getHashEntry(element_hash, element_token);
14231
14232     if (element_value == NULL)          // this is no element
14233     {
14234       element_value = getHashEntry(element_hash, list->token);
14235       if (element_value != NULL)        // combined element found
14236         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14237                            &num_list_entries);
14238       else
14239         print_unknown_token(filename, list->token, num_unknown_tokens++);
14240
14241       free(element_token);
14242
14243       continue;
14244     }
14245
14246     action_value = getHashEntry(action_hash, action_token);
14247
14248     if (action_value != NULL)           // action found
14249     {
14250       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14251                     &num_list_entries);
14252
14253       free(element_token);
14254
14255       continue;
14256     }
14257
14258     direction_value = getHashEntry(direction_hash, direction_token);
14259
14260     if (direction_value != NULL)        // direction found
14261     {
14262       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14263                          &num_list_entries);
14264
14265       free(element_token);
14266
14267       continue;
14268     }
14269
14270     if (strchr(action_token + 1, '.') == NULL)
14271     {
14272       // no further suffixes found -- this is not an action nor direction
14273
14274       element_value = getHashEntry(element_hash, list->token);
14275       if (element_value != NULL)        // combined element found
14276         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14277                            &num_list_entries);
14278       else
14279         print_unknown_token(filename, list->token, num_unknown_tokens++);
14280
14281       free(element_token);
14282
14283       continue;
14284     }
14285
14286     // token has format "<prefix>.<suffix>.<something>"
14287
14288     direction_token = strchr(action_token + 1, '.');
14289
14290     action_token = getStringCopy(action_token);
14291     *strchr(action_token + 1, '.') = '\0';
14292
14293     action_value = getHashEntry(action_hash, action_token);
14294
14295     if (action_value == NULL)           // this is no action
14296     {
14297       element_value = getHashEntry(element_hash, list->token);
14298       if (element_value != NULL)        // combined element found
14299         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14300                            &num_list_entries);
14301       else
14302         print_unknown_token(filename, list->token, num_unknown_tokens++);
14303
14304       free(element_token);
14305       free(action_token);
14306
14307       continue;
14308     }
14309
14310     direction_value = getHashEntry(direction_hash, direction_token);
14311
14312     if (direction_value != NULL)        // direction found
14313     {
14314       add_helpanim_entry(atoi(element_value), atoi(action_value),
14315                          atoi(direction_value), delay, &num_list_entries);
14316
14317       free(element_token);
14318       free(action_token);
14319
14320       continue;
14321     }
14322
14323     // this is no direction
14324
14325     element_value = getHashEntry(element_hash, list->token);
14326     if (element_value != NULL)          // combined element found
14327       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14328                          &num_list_entries);
14329     else
14330       print_unknown_token(filename, list->token, num_unknown_tokens++);
14331
14332     free(element_token);
14333     free(action_token);
14334   }
14335
14336   print_unknown_token_end(num_unknown_tokens);
14337
14338   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14339   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14340
14341   freeSetupFileList(setup_file_list);
14342   freeSetupFileHash(element_hash);
14343   freeSetupFileHash(action_hash);
14344   freeSetupFileHash(direction_hash);
14345
14346 #if 0
14347   for (i = 0; i < num_list_entries; i++)
14348     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14349           EL_NAME(helpanim_info[i].element),
14350           helpanim_info[i].element,
14351           helpanim_info[i].action,
14352           helpanim_info[i].direction,
14353           helpanim_info[i].delay);
14354 #endif
14355 }
14356
14357 void LoadHelpTextInfo(void)
14358 {
14359   char *filename = getHelpTextFilename();
14360   int i;
14361
14362   if (helptext_info != NULL)
14363   {
14364     freeSetupFileHash(helptext_info);
14365     helptext_info = NULL;
14366   }
14367
14368   if (fileExists(filename))
14369     helptext_info = loadSetupFileHash(filename);
14370
14371   if (helptext_info == NULL)
14372   {
14373     // use reliable default values from static configuration
14374     helptext_info = newSetupFileHash();
14375
14376     for (i = 0; helptext_config[i].token; i++)
14377       setHashEntry(helptext_info,
14378                    helptext_config[i].token,
14379                    helptext_config[i].value);
14380   }
14381
14382 #if 0
14383   BEGIN_HASH_ITERATION(helptext_info, itr)
14384   {
14385     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14386           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14387   }
14388   END_HASH_ITERATION(hash, itr)
14389 #endif
14390 }
14391
14392
14393 // ----------------------------------------------------------------------------
14394 // convert levels
14395 // ----------------------------------------------------------------------------
14396
14397 #define MAX_NUM_CONVERT_LEVELS          1000
14398
14399 void ConvertLevels(void)
14400 {
14401   static LevelDirTree *convert_leveldir = NULL;
14402   static int convert_level_nr = -1;
14403   static int num_levels_handled = 0;
14404   static int num_levels_converted = 0;
14405   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14406   int i;
14407
14408   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14409                                                global.convert_leveldir);
14410
14411   if (convert_leveldir == NULL)
14412     Fail("no such level identifier: '%s'", global.convert_leveldir);
14413
14414   leveldir_current = convert_leveldir;
14415
14416   if (global.convert_level_nr != -1)
14417   {
14418     convert_leveldir->first_level = global.convert_level_nr;
14419     convert_leveldir->last_level  = global.convert_level_nr;
14420   }
14421
14422   convert_level_nr = convert_leveldir->first_level;
14423
14424   PrintLine("=", 79);
14425   Print("Converting levels\n");
14426   PrintLine("-", 79);
14427   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14428   Print("Level series name:       '%s'\n", convert_leveldir->name);
14429   Print("Level series author:     '%s'\n", convert_leveldir->author);
14430   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14431   PrintLine("=", 79);
14432   Print("\n");
14433
14434   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14435     levels_failed[i] = FALSE;
14436
14437   while (convert_level_nr <= convert_leveldir->last_level)
14438   {
14439     char *level_filename;
14440     boolean new_level;
14441
14442     level_nr = convert_level_nr++;
14443
14444     Print("Level %03d: ", level_nr);
14445
14446     LoadLevel(level_nr);
14447     if (level.no_level_file || level.no_valid_file)
14448     {
14449       Print("(no level)\n");
14450       continue;
14451     }
14452
14453     Print("converting level ... ");
14454
14455 #if 0
14456     // special case: conversion of some EMC levels as requested by ACME
14457     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14458 #endif
14459
14460     level_filename = getDefaultLevelFilename(level_nr);
14461     new_level = !fileExists(level_filename);
14462
14463     if (new_level)
14464     {
14465       SaveLevel(level_nr);
14466
14467       num_levels_converted++;
14468
14469       Print("converted.\n");
14470     }
14471     else
14472     {
14473       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14474         levels_failed[level_nr] = TRUE;
14475
14476       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14477     }
14478
14479     num_levels_handled++;
14480   }
14481
14482   Print("\n");
14483   PrintLine("=", 79);
14484   Print("Number of levels handled: %d\n", num_levels_handled);
14485   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14486          (num_levels_handled ?
14487           num_levels_converted * 100 / num_levels_handled : 0));
14488   PrintLine("-", 79);
14489   Print("Summary (for automatic parsing by scripts):\n");
14490   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14491          convert_leveldir->identifier, num_levels_converted,
14492          num_levels_handled,
14493          (num_levels_handled ?
14494           num_levels_converted * 100 / num_levels_handled : 0));
14495
14496   if (num_levels_handled != num_levels_converted)
14497   {
14498     Print(", FAILED:");
14499     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14500       if (levels_failed[i])
14501         Print(" %03d", i);
14502   }
14503
14504   Print("\n");
14505   PrintLine("=", 79);
14506
14507   CloseAllAndExit(0);
14508 }
14509
14510
14511 // ----------------------------------------------------------------------------
14512 // create and save images for use in level sketches (raw BMP format)
14513 // ----------------------------------------------------------------------------
14514
14515 void CreateLevelSketchImages(void)
14516 {
14517   Bitmap *bitmap1;
14518   Bitmap *bitmap2;
14519   int i;
14520
14521   InitElementPropertiesGfxElement();
14522
14523   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14524   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14525
14526   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14527   {
14528     int element = getMappedElement(i);
14529     char basename1[16];
14530     char basename2[16];
14531     char *filename1;
14532     char *filename2;
14533
14534     sprintf(basename1, "%04d.bmp", i);
14535     sprintf(basename2, "%04ds.bmp", i);
14536
14537     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14538     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14539
14540     DrawSizedElement(0, 0, element, TILESIZE);
14541     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14542
14543     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14544       Fail("cannot save level sketch image file '%s'", filename1);
14545
14546     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14547     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14548
14549     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14550       Fail("cannot save level sketch image file '%s'", filename2);
14551
14552     free(filename1);
14553     free(filename2);
14554
14555     // create corresponding SQL statements (for normal and small images)
14556     if (i < 1000)
14557     {
14558       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14559       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14560     }
14561
14562     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14563     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14564
14565     // optional: create content for forum level sketch demonstration post
14566     if (options.debug)
14567       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14568   }
14569
14570   FreeBitmap(bitmap1);
14571   FreeBitmap(bitmap2);
14572
14573   if (options.debug)
14574     fprintf(stderr, "\n");
14575
14576   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14577
14578   CloseAllAndExit(0);
14579 }
14580
14581
14582 // ----------------------------------------------------------------------------
14583 // create and save images for element collecting animations (raw BMP format)
14584 // ----------------------------------------------------------------------------
14585
14586 static boolean createCollectImage(int element)
14587 {
14588   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14589 }
14590
14591 void CreateCollectElementImages(void)
14592 {
14593   int i, j;
14594   int num_steps = 8;
14595   int anim_frames = num_steps - 1;
14596   int tile_size = TILESIZE;
14597   int anim_width  = tile_size * anim_frames;
14598   int anim_height = tile_size;
14599   int num_collect_images = 0;
14600   int pos_collect_images = 0;
14601
14602   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14603     if (createCollectImage(i))
14604       num_collect_images++;
14605
14606   Info("Creating %d element collecting animation images ...",
14607        num_collect_images);
14608
14609   int dst_width  = anim_width * 2;
14610   int dst_height = anim_height * num_collect_images / 2;
14611   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14612   char *basename_bmp = "RocksCollect.bmp";
14613   char *basename_png = "RocksCollect.png";
14614   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14615   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14616   int len_filename_bmp = strlen(filename_bmp);
14617   int len_filename_png = strlen(filename_png);
14618   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14619   char cmd_convert[max_command_len];
14620
14621   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14622            filename_bmp,
14623            filename_png);
14624
14625   // force using RGBA surface for destination bitmap
14626   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14627                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14628
14629   dst_bitmap->surface =
14630     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14631
14632   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14633   {
14634     if (!createCollectImage(i))
14635       continue;
14636
14637     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14638     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14639     int graphic = el2img(i);
14640     char *token_name = element_info[i].token_name;
14641     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14642     Bitmap *src_bitmap;
14643     int src_x, src_y;
14644
14645     Info("- creating collecting image for '%s' ...", token_name);
14646
14647     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14648
14649     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14650                tile_size, tile_size, 0, 0);
14651
14652     // force using RGBA surface for temporary bitmap (using transparent black)
14653     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14654                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14655
14656     tmp_bitmap->surface =
14657       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14658
14659     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14660
14661     for (j = 0; j < anim_frames; j++)
14662     {
14663       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14664       int frame_size = frame_size_final * num_steps;
14665       int offset = (tile_size - frame_size_final) / 2;
14666       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14667
14668       while (frame_size > frame_size_final)
14669       {
14670         frame_size /= 2;
14671
14672         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14673
14674         FreeBitmap(frame_bitmap);
14675
14676         frame_bitmap = half_bitmap;
14677       }
14678
14679       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14680                        frame_size_final, frame_size_final,
14681                        dst_x + j * tile_size + offset, dst_y + offset);
14682
14683       FreeBitmap(frame_bitmap);
14684     }
14685
14686     tmp_bitmap->surface_masked = NULL;
14687
14688     FreeBitmap(tmp_bitmap);
14689
14690     pos_collect_images++;
14691   }
14692
14693   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14694     Fail("cannot save element collecting image file '%s'", filename_bmp);
14695
14696   FreeBitmap(dst_bitmap);
14697
14698   Info("Converting image file from BMP to PNG ...");
14699
14700   if (system(cmd_convert) != 0)
14701     Fail("converting image file failed");
14702
14703   unlink(filename_bmp);
14704
14705   Info("Done.");
14706
14707   CloseAllAndExit(0);
14708 }
14709
14710
14711 // ----------------------------------------------------------------------------
14712 // create and save images for custom and group elements (raw BMP format)
14713 // ----------------------------------------------------------------------------
14714
14715 void CreateCustomElementImages(char *directory)
14716 {
14717   char *src_basename = "RocksCE-template.ilbm";
14718   char *dst_basename = "RocksCE.bmp";
14719   char *src_filename = getPath2(directory, src_basename);
14720   char *dst_filename = getPath2(directory, dst_basename);
14721   Bitmap *src_bitmap;
14722   Bitmap *bitmap;
14723   int yoffset_ce = 0;
14724   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14725   int i;
14726
14727   InitVideoDefaults();
14728
14729   ReCreateBitmap(&backbuffer, video.width, video.height);
14730
14731   src_bitmap = LoadImage(src_filename);
14732
14733   bitmap = CreateBitmap(TILEX * 16 * 2,
14734                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14735                         DEFAULT_DEPTH);
14736
14737   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14738   {
14739     int x = i % 16;
14740     int y = i / 16;
14741     int ii = i + 1;
14742     int j;
14743
14744     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14745                TILEX * x, TILEY * y + yoffset_ce);
14746
14747     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14748                TILEX, TILEY,
14749                TILEX * x + TILEX * 16,
14750                TILEY * y + yoffset_ce);
14751
14752     for (j = 2; j >= 0; j--)
14753     {
14754       int c = ii % 10;
14755
14756       BlitBitmap(src_bitmap, bitmap,
14757                  TILEX + c * 7, 0, 6, 10,
14758                  TILEX * x + 6 + j * 7,
14759                  TILEY * y + 11 + yoffset_ce);
14760
14761       BlitBitmap(src_bitmap, bitmap,
14762                  TILEX + c * 8, TILEY, 6, 10,
14763                  TILEX * 16 + TILEX * x + 6 + j * 8,
14764                  TILEY * y + 10 + yoffset_ce);
14765
14766       ii /= 10;
14767     }
14768   }
14769
14770   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14771   {
14772     int x = i % 16;
14773     int y = i / 16;
14774     int ii = i + 1;
14775     int j;
14776
14777     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14778                TILEX * x, TILEY * y + yoffset_ge);
14779
14780     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14781                TILEX, TILEY,
14782                TILEX * x + TILEX * 16,
14783                TILEY * y + yoffset_ge);
14784
14785     for (j = 1; j >= 0; j--)
14786     {
14787       int c = ii % 10;
14788
14789       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14790                  TILEX * x + 6 + j * 10,
14791                  TILEY * y + 11 + yoffset_ge);
14792
14793       BlitBitmap(src_bitmap, bitmap,
14794                  TILEX + c * 8, TILEY + 12, 6, 10,
14795                  TILEX * 16 + TILEX * x + 10 + j * 8,
14796                  TILEY * y + 10 + yoffset_ge);
14797
14798       ii /= 10;
14799     }
14800   }
14801
14802   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14803     Fail("cannot save CE graphics file '%s'", dst_filename);
14804
14805   FreeBitmap(bitmap);
14806
14807   CloseAllAndExit(0);
14808 }