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