added support for sand (dirt) settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
749     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
754     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
759     &li.bd_slime_eats_element_2,        EL_BD_ROCK
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
764     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
769     &li.bd_slime_eats_element_3,        EL_BD_NUT
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
774     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
775   },
776
777   {
778     EL_BD_ACID,                         -1,
779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
780     &li.bd_acid_eats_element,           EL_BD_SAND
781   },
782   {
783     EL_BD_ACID,                         -1,
784     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
785     &li.bd_acid_spread_rate,            3
786   },
787   {
788     EL_BD_ACID,                         -1,
789     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
790     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
791   },
792
793   {
794     EL_BD_BITER,                        -1,
795     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
796     &li.bd_biter_move_delay,            0
797   },
798   {
799     EL_BD_BITER,                        -1,
800     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
801     &li.bd_biter_eats_element,          EL_BD_DIAMOND
802   },
803
804   {
805     EL_BD_BLADDER,                      -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
808   },
809
810   {
811     EL_BD_EXPANDABLE_WALL_ANY,          -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.bd_change_expanding_wall,       FALSE
814   },
815   {
816     EL_BD_EXPANDABLE_WALL_ANY,          -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
818     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
819   },
820
821   {
822     EL_BD_REPLICATOR,                   -1,
823     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
824     &li.bd_replicators_active,          TRUE
825   },
826   {
827     EL_BD_REPLICATOR,                   -1,
828     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
829     &li.bd_replicator_create_delay,     4
830   },
831
832   {
833     EL_BD_CONVEYOR_LEFT,                -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
835     &li.bd_conveyor_belts_active,       TRUE
836   },
837   {
838     EL_BD_CONVEYOR_LEFT,                -1,
839     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
840     &li.bd_conveyor_belts_changed,      FALSE
841   },
842
843   {
844     EL_BD_WATER,                        -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
846     &li.bd_water_cannot_flow_down,      FALSE
847   },
848
849   {
850     EL_BD_NUT,                          -1,
851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
852     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
853   },
854
855   {
856     EL_BD_PNEUMATIC_HAMMER,             -1,
857     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
858     &li.bd_hammer_walls_break_delay,    5
859   },
860   {
861     EL_BD_PNEUMATIC_HAMMER,             -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
863     &li.bd_hammer_walls_reappear,       FALSE
864   },
865   {
866     EL_BD_PNEUMATIC_HAMMER,             -1,
867     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
868     &li.bd_hammer_walls_reappear_delay, 100
869   },
870
871   {
872     EL_BD_SKELETON,                     -1,
873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
874     &li.bd_num_skeletons_needed_for_pot, 5
875   },
876   {
877     EL_BD_SKELETON,                     -1,
878     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
879     &li.bd_skeleton_worth_num_diamonds, 0
880   },
881
882   {
883     EL_BD_SAND,                         -1,
884     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
885     &li.bd_sand_looks_like,             EL_BD_SAND
886   },
887
888   // (the following values are related to various game elements)
889
890   {
891     EL_EMERALD,                         -1,
892     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
893     &li.score[SC_EMERALD],              10
894   },
895
896   {
897     EL_DIAMOND,                         -1,
898     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
899     &li.score[SC_DIAMOND],              10
900   },
901
902   {
903     EL_BUG,                             -1,
904     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
905     &li.score[SC_BUG],                  10
906   },
907
908   {
909     EL_SPACESHIP,                       -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
911     &li.score[SC_SPACESHIP],            10
912   },
913
914   {
915     EL_PACMAN,                          -1,
916     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
917     &li.score[SC_PACMAN],               10
918   },
919
920   {
921     EL_NUT,                             -1,
922     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
923     &li.score[SC_NUT],                  10
924   },
925
926   {
927     EL_DYNAMITE,                        -1,
928     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
929     &li.score[SC_DYNAMITE],             10
930   },
931
932   {
933     EL_KEY_1,                           -1,
934     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
935     &li.score[SC_KEY],                  10
936   },
937
938   {
939     EL_PEARL,                           -1,
940     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
941     &li.score[SC_PEARL],                10
942   },
943
944   {
945     EL_CRYSTAL,                         -1,
946     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
947     &li.score[SC_CRYSTAL],              10
948   },
949
950   // (amoeba values used by R'n'D game engine only)
951   {
952     EL_BD_AMOEBA,                       -1,
953     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
954     &li.amoeba_content,                 EL_DIAMOND
955   },
956   {
957     EL_BD_AMOEBA,                       -1,
958     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
959     &li.amoeba_speed,                   10
960   },
961   {
962     EL_BD_AMOEBA,                       -1,
963     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
964     &li.grow_into_diggable,             TRUE
965   },
966   // (amoeba values used by BD game engine only)
967   {
968     EL_BD_AMOEBA,                       -1,
969     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
970     &li.bd_amoeba_wait_for_hatching,    FALSE
971   },
972   {
973     EL_BD_AMOEBA,                       -1,
974     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
975     &li.bd_amoeba_start_immediately,    TRUE
976   },
977   {
978     EL_BD_AMOEBA,                       -1,
979     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
980     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
981   },
982   {
983     EL_BD_AMOEBA,                       -1,
984     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
985     &li.bd_amoeba_threshold_too_big,    200
986   },
987   {
988     EL_BD_AMOEBA,                       -1,
989     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
990     &li.bd_amoeba_slow_growth_time,     200
991   },
992   {
993     EL_BD_AMOEBA,                       -1,
994     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
995     &li.bd_amoeba_slow_growth_rate,     3
996   },
997   {
998     EL_BD_AMOEBA,                       -1,
999     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1000     &li.bd_amoeba_fast_growth_rate,     25
1001   },
1002   {
1003     EL_BD_AMOEBA,                       -1,
1004     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1005     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1006   },
1007   {
1008     EL_BD_AMOEBA,                       -1,
1009     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1010     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1011   },
1012
1013   {
1014     EL_BD_AMOEBA_2,                     -1,
1015     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1016     &li.bd_amoeba_2_threshold_too_big,  200
1017   },
1018   {
1019     EL_BD_AMOEBA_2,                     -1,
1020     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1021     &li.bd_amoeba_2_slow_growth_time,   200
1022   },
1023   {
1024     EL_BD_AMOEBA_2,                     -1,
1025     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1026     &li.bd_amoeba_2_slow_growth_rate,   3
1027   },
1028   {
1029     EL_BD_AMOEBA_2,                     -1,
1030     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1031     &li.bd_amoeba_2_fast_growth_rate,   25
1032   },
1033   {
1034     EL_BD_AMOEBA_2,                     -1,
1035     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1036     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1037   },
1038   {
1039     EL_BD_AMOEBA_2,                     -1,
1040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1041     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1042   },
1043   {
1044     EL_BD_AMOEBA_2,                     -1,
1045     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1046     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1047   },
1048   {
1049     EL_BD_AMOEBA_2,                     -1,
1050     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1051     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1052   },
1053
1054   {
1055     EL_YAMYAM,                          -1,
1056     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1057     &li.yamyam_content,                 EL_ROCK, NULL,
1058     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1059   },
1060   {
1061     EL_YAMYAM,                          -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_YAMYAM],               10
1064   },
1065
1066   {
1067     EL_ROBOT,                           -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_ROBOT],                10
1070   },
1071   {
1072     EL_ROBOT,                           -1,
1073     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1074     &li.slurp_score,                    10
1075   },
1076
1077   {
1078     EL_ROBOT_WHEEL,                     -1,
1079     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1080     &li.time_wheel,                     10
1081   },
1082
1083   {
1084     EL_MAGIC_WALL,                      -1,
1085     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1086     &li.time_magic_wall,                10
1087   },
1088
1089   {
1090     EL_GAME_OF_LIFE,                    -1,
1091     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1092     &li.game_of_life[0],                2
1093   },
1094   {
1095     EL_GAME_OF_LIFE,                    -1,
1096     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1097     &li.game_of_life[1],                3
1098   },
1099   {
1100     EL_GAME_OF_LIFE,                    -1,
1101     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1102     &li.game_of_life[2],                3
1103   },
1104   {
1105     EL_GAME_OF_LIFE,                    -1,
1106     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1107     &li.game_of_life[3],                3
1108   },
1109   {
1110     EL_GAME_OF_LIFE,                    -1,
1111     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1112     &li.use_life_bugs,                  FALSE
1113   },
1114
1115   {
1116     EL_BIOMAZE,                         -1,
1117     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1118     &li.biomaze[0],                     2
1119   },
1120   {
1121     EL_BIOMAZE,                         -1,
1122     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1123     &li.biomaze[1],                     3
1124   },
1125   {
1126     EL_BIOMAZE,                         -1,
1127     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1128     &li.biomaze[2],                     3
1129   },
1130   {
1131     EL_BIOMAZE,                         -1,
1132     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1133     &li.biomaze[3],                     3
1134   },
1135
1136   {
1137     EL_TIMEGATE_SWITCH,                 -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.time_timegate,                  10
1140   },
1141
1142   {
1143     EL_LIGHT_SWITCH_ACTIVE,             -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1145     &li.time_light,                     10
1146   },
1147
1148   {
1149     EL_SHIELD_NORMAL,                   -1,
1150     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1151     &li.shield_normal_time,             10
1152   },
1153   {
1154     EL_SHIELD_NORMAL,                   -1,
1155     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1156     &li.score[SC_SHIELD],               10
1157   },
1158
1159   {
1160     EL_SHIELD_DEADLY,                   -1,
1161     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1162     &li.shield_deadly_time,             10
1163   },
1164   {
1165     EL_SHIELD_DEADLY,                   -1,
1166     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1167     &li.score[SC_SHIELD],               10
1168   },
1169
1170   {
1171     EL_EXTRA_TIME,                      -1,
1172     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1173     &li.extra_time,                     10
1174   },
1175   {
1176     EL_EXTRA_TIME,                      -1,
1177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1178     &li.extra_time_score,               10
1179   },
1180
1181   {
1182     EL_TIME_ORB_FULL,                   -1,
1183     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1184     &li.time_orb_time,                  10
1185   },
1186   {
1187     EL_TIME_ORB_FULL,                   -1,
1188     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1189     &li.use_time_orb_bug,               FALSE
1190   },
1191
1192   {
1193     EL_SPRING,                          -1,
1194     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1195     &li.use_spring_bug,                 FALSE
1196   },
1197
1198   {
1199     EL_EMC_ANDROID,                     -1,
1200     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1201     &li.android_move_time,              10
1202   },
1203   {
1204     EL_EMC_ANDROID,                     -1,
1205     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1206     &li.android_clone_time,             10
1207   },
1208   {
1209     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1210     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1211     &li.android_clone_element[0],       EL_EMPTY, NULL,
1212     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1213   },
1214   {
1215     EL_EMC_ANDROID,                     -1,
1216     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1217     &li.android_clone_element[0],       EL_EMPTY, NULL,
1218     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1219   },
1220
1221   {
1222     EL_EMC_LENSES,                      -1,
1223     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1224     &li.lenses_score,                   10
1225   },
1226   {
1227     EL_EMC_LENSES,                      -1,
1228     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1229     &li.lenses_time,                    10
1230   },
1231
1232   {
1233     EL_EMC_MAGNIFIER,                   -1,
1234     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1235     &li.magnify_score,                  10
1236   },
1237   {
1238     EL_EMC_MAGNIFIER,                   -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1240     &li.magnify_time,                   10
1241   },
1242
1243   {
1244     EL_EMC_MAGIC_BALL,                  -1,
1245     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1246     &li.ball_time,                      10
1247   },
1248   {
1249     EL_EMC_MAGIC_BALL,                  -1,
1250     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1251     &li.ball_random,                    FALSE
1252   },
1253   {
1254     EL_EMC_MAGIC_BALL,                  -1,
1255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1256     &li.ball_active_initial,            FALSE
1257   },
1258   {
1259     EL_EMC_MAGIC_BALL,                  -1,
1260     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1261     &li.ball_content,                   EL_EMPTY, NULL,
1262     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1263   },
1264
1265   {
1266     EL_SOKOBAN_FIELD_EMPTY,             -1,
1267     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1268     &li.sb_fields_needed,               TRUE
1269   },
1270
1271   {
1272     EL_SOKOBAN_OBJECT,                  -1,
1273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1274     &li.sb_objects_needed,              TRUE
1275   },
1276
1277   {
1278     EL_MM_MCDUFFIN,                     -1,
1279     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1280     &li.mm_laser_red,                   FALSE
1281   },
1282   {
1283     EL_MM_MCDUFFIN,                     -1,
1284     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1285     &li.mm_laser_green,                 FALSE
1286   },
1287   {
1288     EL_MM_MCDUFFIN,                     -1,
1289     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1290     &li.mm_laser_blue,                  TRUE
1291   },
1292
1293   {
1294     EL_DF_LASER,                        -1,
1295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1296     &li.df_laser_red,                   TRUE
1297   },
1298   {
1299     EL_DF_LASER,                        -1,
1300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1301     &li.df_laser_green,                 TRUE
1302   },
1303   {
1304     EL_DF_LASER,                        -1,
1305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1306     &li.df_laser_blue,                  FALSE
1307   },
1308
1309   {
1310     EL_MM_FUSE_ACTIVE,                  -1,
1311     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1312     &li.mm_time_fuse,                   25
1313   },
1314   {
1315     EL_MM_BOMB,                         -1,
1316     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1317     &li.mm_time_bomb,                   75
1318   },
1319
1320   {
1321     EL_MM_GRAY_BALL,                    -1,
1322     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1323     &li.mm_time_ball,                   75
1324   },
1325   {
1326     EL_MM_GRAY_BALL,                    -1,
1327     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1328     &li.mm_ball_choice_mode,            ANIM_RANDOM
1329   },
1330   {
1331     EL_MM_GRAY_BALL,                    -1,
1332     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1333     &li.mm_ball_content,                EL_EMPTY, NULL,
1334     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1335   },
1336   {
1337     EL_MM_GRAY_BALL,                    -1,
1338     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1339     &li.rotate_mm_ball_content,         TRUE
1340   },
1341   {
1342     EL_MM_GRAY_BALL,                    -1,
1343     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1344     &li.explode_mm_ball,                FALSE
1345   },
1346
1347   {
1348     EL_MM_STEEL_BLOCK,                  -1,
1349     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1350     &li.mm_time_block,                  75
1351   },
1352   {
1353     EL_MM_LIGHTBALL,                    -1,
1354     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1355     &li.score[SC_ELEM_BONUS],           10
1356   },
1357
1358   {
1359     -1,                                 -1,
1360     -1,                                 -1,
1361     NULL,                               -1
1362   }
1363 };
1364
1365 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1366 {
1367   {
1368     -1,                                 -1,
1369     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1370     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1371   },
1372   {
1373     -1,                                 -1,
1374     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1375     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1376   },
1377
1378   {
1379     -1,                                 -1,
1380     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1381     &xx_envelope.autowrap,              FALSE
1382   },
1383   {
1384     -1,                                 -1,
1385     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1386     &xx_envelope.centered,              FALSE
1387   },
1388
1389   {
1390     -1,                                 -1,
1391     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1392     &xx_envelope.text,                  -1, NULL,
1393     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1394     &xx_default_string_empty[0]
1395   },
1396
1397   {
1398     -1,                                 -1,
1399     -1,                                 -1,
1400     NULL,                               -1
1401   }
1402 };
1403
1404 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1405 {
1406   {
1407     -1,                                 -1,
1408     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1409     &xx_ei.description[0],              -1,
1410     &yy_ei.description[0],
1411     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1412     &xx_default_description[0]
1413   },
1414
1415   {
1416     -1,                                 -1,
1417     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1418     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1419     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1420   },
1421 #if ENABLE_RESERVED_CODE
1422   // (reserved for later use)
1423   {
1424     -1,                                 -1,
1425     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1426     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1427     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1428   },
1429 #endif
1430
1431   {
1432     -1,                                 -1,
1433     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1434     &xx_ei.use_gfx_element,             FALSE,
1435     &yy_ei.use_gfx_element
1436   },
1437   {
1438     -1,                                 -1,
1439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1440     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1441     &yy_ei.gfx_element_initial
1442   },
1443
1444   {
1445     -1,                                 -1,
1446     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1447     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1448     &yy_ei.access_direction
1449   },
1450
1451   {
1452     -1,                                 -1,
1453     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1454     &xx_ei.collect_score_initial,       10,
1455     &yy_ei.collect_score_initial
1456   },
1457   {
1458     -1,                                 -1,
1459     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1460     &xx_ei.collect_count_initial,       1,
1461     &yy_ei.collect_count_initial
1462   },
1463
1464   {
1465     -1,                                 -1,
1466     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1467     &xx_ei.ce_value_fixed_initial,      0,
1468     &yy_ei.ce_value_fixed_initial
1469   },
1470   {
1471     -1,                                 -1,
1472     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1473     &xx_ei.ce_value_random_initial,     0,
1474     &yy_ei.ce_value_random_initial
1475   },
1476   {
1477     -1,                                 -1,
1478     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1479     &xx_ei.use_last_ce_value,           FALSE,
1480     &yy_ei.use_last_ce_value
1481   },
1482
1483   {
1484     -1,                                 -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1486     &xx_ei.push_delay_fixed,            8,
1487     &yy_ei.push_delay_fixed
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1492     &xx_ei.push_delay_random,           8,
1493     &yy_ei.push_delay_random
1494   },
1495   {
1496     -1,                                 -1,
1497     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1498     &xx_ei.drop_delay_fixed,            0,
1499     &yy_ei.drop_delay_fixed
1500   },
1501   {
1502     -1,                                 -1,
1503     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1504     &xx_ei.drop_delay_random,           0,
1505     &yy_ei.drop_delay_random
1506   },
1507   {
1508     -1,                                 -1,
1509     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1510     &xx_ei.move_delay_fixed,            0,
1511     &yy_ei.move_delay_fixed
1512   },
1513   {
1514     -1,                                 -1,
1515     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1516     &xx_ei.move_delay_random,           0,
1517     &yy_ei.move_delay_random
1518   },
1519   {
1520     -1,                                 -1,
1521     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1522     &xx_ei.step_delay_fixed,            0,
1523     &yy_ei.step_delay_fixed
1524   },
1525   {
1526     -1,                                 -1,
1527     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1528     &xx_ei.step_delay_random,           0,
1529     &yy_ei.step_delay_random
1530   },
1531
1532   {
1533     -1,                                 -1,
1534     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1535     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1536     &yy_ei.move_pattern
1537   },
1538   {
1539     -1,                                 -1,
1540     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1541     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1542     &yy_ei.move_direction_initial
1543   },
1544   {
1545     -1,                                 -1,
1546     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1547     &xx_ei.move_stepsize,               TILEX / 8,
1548     &yy_ei.move_stepsize
1549   },
1550
1551   {
1552     -1,                                 -1,
1553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1554     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1555     &yy_ei.move_enter_element
1556   },
1557   {
1558     -1,                                 -1,
1559     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1560     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1561     &yy_ei.move_leave_element
1562   },
1563   {
1564     -1,                                 -1,
1565     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1566     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1567     &yy_ei.move_leave_type
1568   },
1569
1570   {
1571     -1,                                 -1,
1572     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1573     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1574     &yy_ei.slippery_type
1575   },
1576
1577   {
1578     -1,                                 -1,
1579     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1580     &xx_ei.explosion_type,              EXPLODES_3X3,
1581     &yy_ei.explosion_type
1582   },
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1586     &xx_ei.explosion_delay,             16,
1587     &yy_ei.explosion_delay
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1592     &xx_ei.ignition_delay,              8,
1593     &yy_ei.ignition_delay
1594   },
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1599     &xx_ei.content,                     EL_EMPTY_SPACE,
1600     &yy_ei.content,
1601     &xx_num_contents,                   1, 1
1602   },
1603
1604   // ---------- "num_change_pages" must be the last entry ---------------------
1605
1606   {
1607     -1,                                 SAVE_CONF_ALWAYS,
1608     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1609     &xx_ei.num_change_pages,            1,
1610     &yy_ei.num_change_pages
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     -1,                                 -1,
1616     NULL,                               -1,
1617     NULL
1618   }
1619 };
1620
1621 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1622 {
1623   // ---------- "current_change_page" must be the first entry -----------------
1624
1625   {
1626     -1,                                 SAVE_CONF_ALWAYS,
1627     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1628     &xx_current_change_page,            -1
1629   },
1630
1631   // ---------- (the remaining entries can be in any order) -------------------
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1636     &xx_change.can_change,              FALSE
1637   },
1638
1639   {
1640     -1,                                 -1,
1641     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1642     &xx_event_bits[0],                  0
1643   },
1644   {
1645     -1,                                 -1,
1646     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1647     &xx_event_bits[1],                  0
1648   },
1649
1650   {
1651     -1,                                 -1,
1652     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1653     &xx_change.trigger_player,          CH_PLAYER_ANY
1654   },
1655   {
1656     -1,                                 -1,
1657     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1658     &xx_change.trigger_side,            CH_SIDE_ANY
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1663     &xx_change.trigger_page,            CH_PAGE_ANY
1664   },
1665
1666   {
1667     -1,                                 -1,
1668     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1669     &xx_change.target_element,          EL_EMPTY_SPACE
1670   },
1671
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1675     &xx_change.delay_fixed,             0
1676   },
1677   {
1678     -1,                                 -1,
1679     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1680     &xx_change.delay_random,            0
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1685     &xx_change.delay_frames,            FRAMES_PER_SECOND
1686   },
1687
1688   {
1689     -1,                                 -1,
1690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1691     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1692   },
1693
1694   {
1695     -1,                                 -1,
1696     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1697     &xx_change.explode,                 FALSE
1698   },
1699   {
1700     -1,                                 -1,
1701     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1702     &xx_change.use_target_content,      FALSE
1703   },
1704   {
1705     -1,                                 -1,
1706     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1707     &xx_change.only_if_complete,        FALSE
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1712     &xx_change.use_random_replace,      FALSE
1713   },
1714   {
1715     -1,                                 -1,
1716     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1717     &xx_change.random_percentage,       100
1718   },
1719   {
1720     -1,                                 -1,
1721     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1722     &xx_change.replace_when,            CP_WHEN_EMPTY
1723   },
1724
1725   {
1726     -1,                                 -1,
1727     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1728     &xx_change.has_action,              FALSE
1729   },
1730   {
1731     -1,                                 -1,
1732     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1733     &xx_change.action_type,             CA_NO_ACTION
1734   },
1735   {
1736     -1,                                 -1,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1738     &xx_change.action_mode,             CA_MODE_UNDEFINED
1739   },
1740   {
1741     -1,                                 -1,
1742     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1743     &xx_change.action_arg,              CA_ARG_UNDEFINED
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1749     &xx_change.action_element,          EL_EMPTY_SPACE
1750   },
1751
1752   {
1753     -1,                                 -1,
1754     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1755     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1756     &xx_num_contents,                   1, 1
1757   },
1758
1759   {
1760     -1,                                 -1,
1761     -1,                                 -1,
1762     NULL,                               -1
1763   }
1764 };
1765
1766 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1767 {
1768   {
1769     -1,                                 -1,
1770     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1771     &xx_ei.description[0],              -1, NULL,
1772     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1773     &xx_default_description[0]
1774   },
1775
1776   {
1777     -1,                                 -1,
1778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1779     &xx_ei.use_gfx_element,             FALSE
1780   },
1781   {
1782     -1,                                 -1,
1783     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1784     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1785   },
1786
1787   {
1788     -1,                                 -1,
1789     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1790     &xx_group.choice_mode,              ANIM_RANDOM
1791   },
1792
1793   {
1794     -1,                                 -1,
1795     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1796     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1797     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1798   },
1799
1800   {
1801     -1,                                 -1,
1802     -1,                                 -1,
1803     NULL,                               -1
1804   }
1805 };
1806
1807 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1808 {
1809   {
1810     -1,                                 -1,
1811     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1812     &xx_ei.use_gfx_element,             FALSE
1813   },
1814   {
1815     -1,                                 -1,
1816     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1817     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1818   },
1819
1820   {
1821     -1,                                 -1,
1822     -1,                                 -1,
1823     NULL,                               -1
1824   }
1825 };
1826
1827 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1828 {
1829   {
1830     EL_PLAYER_1,                        -1,
1831     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1832     &li.block_snap_field,               TRUE
1833   },
1834   {
1835     EL_PLAYER_1,                        -1,
1836     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1837     &li.continuous_snapping,            TRUE
1838   },
1839   {
1840     EL_PLAYER_1,                        -1,
1841     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1842     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1843   },
1844   {
1845     EL_PLAYER_1,                        -1,
1846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1847     &li.use_start_element[0],           FALSE
1848   },
1849   {
1850     EL_PLAYER_1,                        -1,
1851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1852     &li.start_element[0],               EL_PLAYER_1
1853   },
1854   {
1855     EL_PLAYER_1,                        -1,
1856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1857     &li.use_artwork_element[0],         FALSE
1858   },
1859   {
1860     EL_PLAYER_1,                        -1,
1861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1862     &li.artwork_element[0],             EL_PLAYER_1
1863   },
1864   {
1865     EL_PLAYER_1,                        -1,
1866     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1867     &li.use_explosion_element[0],       FALSE
1868   },
1869   {
1870     EL_PLAYER_1,                        -1,
1871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1872     &li.explosion_element[0],           EL_PLAYER_1
1873   },
1874
1875   {
1876     -1,                                 -1,
1877     -1,                                 -1,
1878     NULL,                               -1
1879   }
1880 };
1881
1882 static struct
1883 {
1884   int filetype;
1885   char *id;
1886 }
1887 filetype_id_list[] =
1888 {
1889   { LEVEL_FILE_TYPE_RND,        "RND"   },
1890   { LEVEL_FILE_TYPE_BD,         "BD"    },
1891   { LEVEL_FILE_TYPE_EM,         "EM"    },
1892   { LEVEL_FILE_TYPE_SP,         "SP"    },
1893   { LEVEL_FILE_TYPE_DX,         "DX"    },
1894   { LEVEL_FILE_TYPE_SB,         "SB"    },
1895   { LEVEL_FILE_TYPE_DC,         "DC"    },
1896   { LEVEL_FILE_TYPE_MM,         "MM"    },
1897   { LEVEL_FILE_TYPE_MM,         "DF"    },
1898   { -1,                         NULL    },
1899 };
1900
1901
1902 // ============================================================================
1903 // level file functions
1904 // ============================================================================
1905
1906 static boolean check_special_flags(char *flag)
1907 {
1908   if (strEqual(options.special_flags, flag) ||
1909       strEqual(leveldir_current->special_flags, flag))
1910     return TRUE;
1911
1912   return FALSE;
1913 }
1914
1915 static struct DateInfo getCurrentDate(void)
1916 {
1917   time_t epoch_seconds = time(NULL);
1918   struct tm *now = localtime(&epoch_seconds);
1919   struct DateInfo date;
1920
1921   date.year  = now->tm_year + 1900;
1922   date.month = now->tm_mon  + 1;
1923   date.day   = now->tm_mday;
1924
1925   date.src   = DATE_SRC_CLOCK;
1926
1927   return date;
1928 }
1929
1930 static void resetEventFlags(struct ElementChangeInfo *change)
1931 {
1932   int i;
1933
1934   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1935     change->has_event[i] = FALSE;
1936 }
1937
1938 static void resetEventBits(void)
1939 {
1940   int i;
1941
1942   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1943     xx_event_bits[i] = 0;
1944 }
1945
1946 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1947 {
1948   int i;
1949
1950   /* important: only change event flag if corresponding event bit is set
1951      (this is because all xx_event_bits[] values are loaded separately,
1952      and all xx_event_bits[] values are set back to zero before loading
1953      another value xx_event_bits[x] (each value representing 32 flags)) */
1954
1955   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1956     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1957       change->has_event[i] = TRUE;
1958 }
1959
1960 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1961 {
1962   int i;
1963
1964   /* in contrast to the above function setEventFlagsFromEventBits(), it
1965      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1966      depending on the corresponding change->has_event[i] values here, as
1967      all xx_event_bits[] values are reset in resetEventBits() before */
1968
1969   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1970     if (change->has_event[i])
1971       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1972 }
1973
1974 static char *getDefaultElementDescription(struct ElementInfo *ei)
1975 {
1976   static char description[MAX_ELEMENT_NAME_LEN + 1];
1977   char *default_description = (ei->custom_description != NULL ?
1978                                ei->custom_description :
1979                                ei->editor_description);
1980   int i;
1981
1982   // always start with reliable default values
1983   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1984     description[i] = '\0';
1985
1986   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1987   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1988
1989   return &description[0];
1990 }
1991
1992 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1993 {
1994   char *default_description = getDefaultElementDescription(ei);
1995   int i;
1996
1997   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1998     ei->description[i] = default_description[i];
1999 }
2000
2001 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2002 {
2003   int i;
2004
2005   for (i = 0; conf[i].data_type != -1; i++)
2006   {
2007     int default_value = conf[i].default_value;
2008     int data_type = conf[i].data_type;
2009     int conf_type = conf[i].conf_type;
2010     int byte_mask = conf_type & CONF_MASK_BYTES;
2011
2012     if (byte_mask == CONF_MASK_MULTI_BYTES)
2013     {
2014       int default_num_entities = conf[i].default_num_entities;
2015       int max_num_entities = conf[i].max_num_entities;
2016
2017       *(int *)(conf[i].num_entities) = default_num_entities;
2018
2019       if (data_type == TYPE_STRING)
2020       {
2021         char *default_string = conf[i].default_string;
2022         char *string = (char *)(conf[i].value);
2023
2024         strncpy(string, default_string, max_num_entities);
2025       }
2026       else if (data_type == TYPE_ELEMENT_LIST)
2027       {
2028         int *element_array = (int *)(conf[i].value);
2029         int j;
2030
2031         for (j = 0; j < max_num_entities; j++)
2032           element_array[j] = default_value;
2033       }
2034       else if (data_type == TYPE_CONTENT_LIST)
2035       {
2036         struct Content *content = (struct Content *)(conf[i].value);
2037         int c, x, y;
2038
2039         for (c = 0; c < max_num_entities; c++)
2040           for (y = 0; y < 3; y++)
2041             for (x = 0; x < 3; x++)
2042               content[c].e[x][y] = default_value;
2043       }
2044     }
2045     else        // constant size configuration data (1, 2 or 4 bytes)
2046     {
2047       if (data_type == TYPE_BOOLEAN)
2048         *(boolean *)(conf[i].value) = default_value;
2049       else
2050         *(int *)    (conf[i].value) = default_value;
2051     }
2052   }
2053 }
2054
2055 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2056 {
2057   int i;
2058
2059   for (i = 0; conf[i].data_type != -1; i++)
2060   {
2061     int data_type = conf[i].data_type;
2062     int conf_type = conf[i].conf_type;
2063     int byte_mask = conf_type & CONF_MASK_BYTES;
2064
2065     if (byte_mask == CONF_MASK_MULTI_BYTES)
2066     {
2067       int max_num_entities = conf[i].max_num_entities;
2068
2069       if (data_type == TYPE_STRING)
2070       {
2071         char *string      = (char *)(conf[i].value);
2072         char *string_copy = (char *)(conf[i].value_copy);
2073
2074         strncpy(string_copy, string, max_num_entities);
2075       }
2076       else if (data_type == TYPE_ELEMENT_LIST)
2077       {
2078         int *element_array      = (int *)(conf[i].value);
2079         int *element_array_copy = (int *)(conf[i].value_copy);
2080         int j;
2081
2082         for (j = 0; j < max_num_entities; j++)
2083           element_array_copy[j] = element_array[j];
2084       }
2085       else if (data_type == TYPE_CONTENT_LIST)
2086       {
2087         struct Content *content      = (struct Content *)(conf[i].value);
2088         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2089         int c, x, y;
2090
2091         for (c = 0; c < max_num_entities; c++)
2092           for (y = 0; y < 3; y++)
2093             for (x = 0; x < 3; x++)
2094               content_copy[c].e[x][y] = content[c].e[x][y];
2095       }
2096     }
2097     else        // constant size configuration data (1, 2 or 4 bytes)
2098     {
2099       if (data_type == TYPE_BOOLEAN)
2100         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2101       else
2102         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2103     }
2104   }
2105 }
2106
2107 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2108 {
2109   int i;
2110
2111   xx_ei = *ei_from;     // copy element data into temporary buffer
2112   yy_ei = *ei_to;       // copy element data into temporary buffer
2113
2114   copyConfigFromConfigList(chunk_config_CUSX_base);
2115
2116   *ei_from = xx_ei;
2117   *ei_to   = yy_ei;
2118
2119   // ---------- reinitialize and copy change pages ----------
2120
2121   ei_to->num_change_pages = ei_from->num_change_pages;
2122   ei_to->current_change_page = ei_from->current_change_page;
2123
2124   setElementChangePages(ei_to, ei_to->num_change_pages);
2125
2126   for (i = 0; i < ei_to->num_change_pages; i++)
2127     ei_to->change_page[i] = ei_from->change_page[i];
2128
2129   // ---------- copy group element info ----------
2130   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2131     *ei_to->group = *ei_from->group;
2132
2133   // mark this custom element as modified
2134   ei_to->modified_settings = TRUE;
2135 }
2136
2137 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2138 {
2139   int change_page_size = sizeof(struct ElementChangeInfo);
2140
2141   ei->num_change_pages = MAX(1, change_pages);
2142
2143   ei->change_page =
2144     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2145
2146   if (ei->current_change_page >= ei->num_change_pages)
2147     ei->current_change_page = ei->num_change_pages - 1;
2148
2149   ei->change = &ei->change_page[ei->current_change_page];
2150 }
2151
2152 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2153 {
2154   xx_change = *change;          // copy change data into temporary buffer
2155
2156   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2157
2158   *change = xx_change;
2159
2160   resetEventFlags(change);
2161
2162   change->direct_action = 0;
2163   change->other_action = 0;
2164
2165   change->pre_change_function = NULL;
2166   change->change_function = NULL;
2167   change->post_change_function = NULL;
2168 }
2169
2170 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2171 {
2172   int i, x, y;
2173
2174   li = *level;          // copy level data into temporary buffer
2175   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2176   *level = li;          // copy temporary buffer back to level data
2177
2178   setLevelInfoToDefaults_BD();
2179   setLevelInfoToDefaults_EM();
2180   setLevelInfoToDefaults_SP();
2181   setLevelInfoToDefaults_MM();
2182
2183   level->native_bd_level = &native_bd_level;
2184   level->native_em_level = &native_em_level;
2185   level->native_sp_level = &native_sp_level;
2186   level->native_mm_level = &native_mm_level;
2187
2188   level->file_version = FILE_VERSION_ACTUAL;
2189   level->game_version = GAME_VERSION_ACTUAL;
2190
2191   level->creation_date = getCurrentDate();
2192
2193   level->encoding_16bit_field  = TRUE;
2194   level->encoding_16bit_yamyam = TRUE;
2195   level->encoding_16bit_amoeba = TRUE;
2196
2197   // clear level name and level author string buffers
2198   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2199     level->name[i] = '\0';
2200   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2201     level->author[i] = '\0';
2202
2203   // set level name and level author to default values
2204   strcpy(level->name, NAMELESS_LEVEL_NAME);
2205   strcpy(level->author, ANONYMOUS_NAME);
2206
2207   // set level playfield to playable default level with player and exit
2208   for (x = 0; x < MAX_LEV_FIELDX; x++)
2209     for (y = 0; y < MAX_LEV_FIELDY; y++)
2210       level->field[x][y] = EL_SAND;
2211
2212   level->field[0][0] = EL_PLAYER_1;
2213   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2214
2215   BorderElement = EL_STEELWALL;
2216
2217   // detect custom elements when loading them
2218   level->file_has_custom_elements = FALSE;
2219
2220   // set all bug compatibility flags to "false" => do not emulate this bug
2221   level->use_action_after_change_bug = FALSE;
2222
2223   if (leveldir_current)
2224   {
2225     // try to determine better author name than 'anonymous'
2226     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2227     {
2228       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2229       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2230     }
2231     else
2232     {
2233       switch (LEVELCLASS(leveldir_current))
2234       {
2235         case LEVELCLASS_TUTORIAL:
2236           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2237           break;
2238
2239         case LEVELCLASS_CONTRIB:
2240           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2241           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2242           break;
2243
2244         case LEVELCLASS_PRIVATE:
2245           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2246           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2247           break;
2248
2249         default:
2250           // keep default value
2251           break;
2252       }
2253     }
2254   }
2255 }
2256
2257 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2258 {
2259   static boolean clipboard_elements_initialized = FALSE;
2260   int i;
2261
2262   InitElementPropertiesStatic();
2263
2264   li = *level;          // copy level data into temporary buffer
2265   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2266   *level = li;          // copy temporary buffer back to level data
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269   {
2270     int element = i;
2271     struct ElementInfo *ei = &element_info[element];
2272
2273     if (element == EL_MM_GRAY_BALL)
2274     {
2275       struct LevelInfo_MM *level_mm = level->native_mm_level;
2276       int j;
2277
2278       for (j = 0; j < level->num_mm_ball_contents; j++)
2279         level->mm_ball_content[j] =
2280           map_element_MM_to_RND(level_mm->ball_content[j]);
2281     }
2282
2283     // never initialize clipboard elements after the very first time
2284     // (to be able to use clipboard elements between several levels)
2285     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2286       continue;
2287
2288     if (IS_ENVELOPE(element))
2289     {
2290       int envelope_nr = element - EL_ENVELOPE_1;
2291
2292       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2293
2294       level->envelope[envelope_nr] = xx_envelope;
2295     }
2296
2297     if (IS_CUSTOM_ELEMENT(element) ||
2298         IS_GROUP_ELEMENT(element) ||
2299         IS_INTERNAL_ELEMENT(element))
2300     {
2301       xx_ei = *ei;      // copy element data into temporary buffer
2302
2303       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2304
2305       *ei = xx_ei;
2306     }
2307
2308     setElementChangePages(ei, 1);
2309     setElementChangeInfoToDefaults(ei->change);
2310
2311     if (IS_CUSTOM_ELEMENT(element) ||
2312         IS_GROUP_ELEMENT(element))
2313     {
2314       setElementDescriptionToDefault(ei);
2315
2316       ei->modified_settings = FALSE;
2317     }
2318
2319     if (IS_CUSTOM_ELEMENT(element) ||
2320         IS_INTERNAL_ELEMENT(element))
2321     {
2322       // internal values used in level editor
2323
2324       ei->access_type = 0;
2325       ei->access_layer = 0;
2326       ei->access_protected = 0;
2327       ei->walk_to_action = 0;
2328       ei->smash_targets = 0;
2329       ei->deadliness = 0;
2330
2331       ei->can_explode_by_fire = FALSE;
2332       ei->can_explode_smashed = FALSE;
2333       ei->can_explode_impact = FALSE;
2334
2335       ei->current_change_page = 0;
2336     }
2337
2338     if (IS_GROUP_ELEMENT(element) ||
2339         IS_INTERNAL_ELEMENT(element))
2340     {
2341       struct ElementGroupInfo *group;
2342
2343       // initialize memory for list of elements in group
2344       if (ei->group == NULL)
2345         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2346
2347       group = ei->group;
2348
2349       xx_group = *group;        // copy group data into temporary buffer
2350
2351       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2352
2353       *group = xx_group;
2354     }
2355
2356     if (IS_EMPTY_ELEMENT(element) ||
2357         IS_INTERNAL_ELEMENT(element))
2358     {
2359       xx_ei = *ei;              // copy element data into temporary buffer
2360
2361       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2362
2363       *ei = xx_ei;
2364     }
2365   }
2366
2367   clipboard_elements_initialized = TRUE;
2368 }
2369
2370 static void setLevelInfoToDefaults(struct LevelInfo *level,
2371                                    boolean level_info_only,
2372                                    boolean reset_file_status)
2373 {
2374   setLevelInfoToDefaults_Level(level);
2375
2376   if (!level_info_only)
2377     setLevelInfoToDefaults_Elements(level);
2378
2379   if (reset_file_status)
2380   {
2381     level->no_valid_file = FALSE;
2382     level->no_level_file = FALSE;
2383   }
2384
2385   level->changed = FALSE;
2386 }
2387
2388 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2389 {
2390   level_file_info->nr = 0;
2391   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2392   level_file_info->packed = FALSE;
2393
2394   setString(&level_file_info->basename, NULL);
2395   setString(&level_file_info->filename, NULL);
2396 }
2397
2398 int getMappedElement_SB(int, boolean);
2399
2400 static void ActivateLevelTemplate(void)
2401 {
2402   int x, y;
2403
2404   if (check_special_flags("load_xsb_to_ces"))
2405   {
2406     // fill smaller playfields with padding "beyond border wall" elements
2407     if (level.fieldx < level_template.fieldx ||
2408         level.fieldy < level_template.fieldy)
2409     {
2410       short field[level.fieldx][level.fieldy];
2411       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2412       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2413       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2414       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2415
2416       // copy old playfield (which is smaller than the visible area)
2417       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2418         field[x][y] = level.field[x][y];
2419
2420       // fill new, larger playfield with "beyond border wall" elements
2421       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2422         level.field[x][y] = getMappedElement_SB('_', TRUE);
2423
2424       // copy the old playfield to the middle of the new playfield
2425       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2426         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2427
2428       level.fieldx = new_fieldx;
2429       level.fieldy = new_fieldy;
2430     }
2431   }
2432
2433   // Currently there is no special action needed to activate the template
2434   // data, because 'element_info' property settings overwrite the original
2435   // level data, while all other variables do not change.
2436
2437   // Exception: 'from_level_template' elements in the original level playfield
2438   // are overwritten with the corresponding elements at the same position in
2439   // playfield from the level template.
2440
2441   for (x = 0; x < level.fieldx; x++)
2442     for (y = 0; y < level.fieldy; y++)
2443       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2444         level.field[x][y] = level_template.field[x][y];
2445
2446   if (check_special_flags("load_xsb_to_ces"))
2447   {
2448     struct LevelInfo level_backup = level;
2449
2450     // overwrite all individual level settings from template level settings
2451     level = level_template;
2452
2453     // restore level file info
2454     level.file_info = level_backup.file_info;
2455
2456     // restore playfield size
2457     level.fieldx = level_backup.fieldx;
2458     level.fieldy = level_backup.fieldy;
2459
2460     // restore playfield content
2461     for (x = 0; x < level.fieldx; x++)
2462       for (y = 0; y < level.fieldy; y++)
2463         level.field[x][y] = level_backup.field[x][y];
2464
2465     // restore name and author from individual level
2466     strcpy(level.name,   level_backup.name);
2467     strcpy(level.author, level_backup.author);
2468
2469     // restore flag "use_custom_template"
2470     level.use_custom_template = level_backup.use_custom_template;
2471   }
2472 }
2473
2474 static boolean checkForPackageFromBasename_BD(char *basename)
2475 {
2476   // check for native BD level file extensions
2477   if (!strSuffixLower(basename, ".bd") &&
2478       !strSuffixLower(basename, ".bdr") &&
2479       !strSuffixLower(basename, ".brc") &&
2480       !strSuffixLower(basename, ".gds"))
2481     return FALSE;
2482
2483   // check for standard single-level BD files (like "001.bd")
2484   if (strSuffixLower(basename, ".bd") &&
2485       strlen(basename) == 6 &&
2486       basename[0] >= '0' && basename[0] <= '9' &&
2487       basename[1] >= '0' && basename[1] <= '9' &&
2488       basename[2] >= '0' && basename[2] <= '9')
2489     return FALSE;
2490
2491   // this is a level package in native BD file format
2492   return TRUE;
2493 }
2494
2495 static char *getLevelFilenameFromBasename(char *basename)
2496 {
2497   static char *filename = NULL;
2498
2499   checked_free(filename);
2500
2501   filename = getPath2(getCurrentLevelDir(), basename);
2502
2503   return filename;
2504 }
2505
2506 static int getFileTypeFromBasename(char *basename)
2507 {
2508   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2509
2510   static char *filename = NULL;
2511   struct stat file_status;
2512
2513   // ---------- try to determine file type from filename ----------
2514
2515   // check for typical filename of a Supaplex level package file
2516   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2517     return LEVEL_FILE_TYPE_SP;
2518
2519   // check for typical filename of a Diamond Caves II level package file
2520   if (strSuffixLower(basename, ".dc") ||
2521       strSuffixLower(basename, ".dc2"))
2522     return LEVEL_FILE_TYPE_DC;
2523
2524   // check for typical filename of a Sokoban level package file
2525   if (strSuffixLower(basename, ".xsb") &&
2526       strchr(basename, '%') == NULL)
2527     return LEVEL_FILE_TYPE_SB;
2528
2529   // check for typical filename of a Boulder Dash (GDash) level package file
2530   if (checkForPackageFromBasename_BD(basename))
2531     return LEVEL_FILE_TYPE_BD;
2532
2533   // ---------- try to determine file type from filesize ----------
2534
2535   checked_free(filename);
2536   filename = getPath2(getCurrentLevelDir(), basename);
2537
2538   if (stat(filename, &file_status) == 0)
2539   {
2540     // check for typical filesize of a Supaplex level package file
2541     if (file_status.st_size == 170496)
2542       return LEVEL_FILE_TYPE_SP;
2543   }
2544
2545   return LEVEL_FILE_TYPE_UNKNOWN;
2546 }
2547
2548 static int getFileTypeFromMagicBytes(char *filename, int type)
2549 {
2550   File *file;
2551
2552   if ((file = openFile(filename, MODE_READ)))
2553   {
2554     char chunk_name[CHUNK_ID_LEN + 1];
2555
2556     getFileChunkBE(file, chunk_name, NULL);
2557
2558     if (strEqual(chunk_name, "MMII") ||
2559         strEqual(chunk_name, "MIRR"))
2560       type = LEVEL_FILE_TYPE_MM;
2561
2562     closeFile(file);
2563   }
2564
2565   return type;
2566 }
2567
2568 static boolean checkForPackageFromBasename(char *basename)
2569 {
2570   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2571   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2572
2573   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2574 }
2575
2576 static char *getSingleLevelBasenameExt(int nr, char *extension)
2577 {
2578   static char basename[MAX_FILENAME_LEN];
2579
2580   if (nr < 0)
2581     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2582   else
2583     sprintf(basename, "%03d.%s", nr, extension);
2584
2585   return basename;
2586 }
2587
2588 static char *getSingleLevelBasename(int nr)
2589 {
2590   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2591 }
2592
2593 static char *getPackedLevelBasename(int type)
2594 {
2595   static char basename[MAX_FILENAME_LEN];
2596   char *directory = getCurrentLevelDir();
2597   Directory *dir;
2598   DirectoryEntry *dir_entry;
2599
2600   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2601
2602   if ((dir = openDirectory(directory)) == NULL)
2603   {
2604     Warn("cannot read current level directory '%s'", directory);
2605
2606     return basename;
2607   }
2608
2609   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2610   {
2611     char *entry_basename = dir_entry->basename;
2612     int entry_type = getFileTypeFromBasename(entry_basename);
2613
2614     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2615     {
2616       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2617           type == entry_type)
2618       {
2619         strcpy(basename, entry_basename);
2620
2621         break;
2622       }
2623     }
2624   }
2625
2626   closeDirectory(dir);
2627
2628   return basename;
2629 }
2630
2631 static char *getSingleLevelFilename(int nr)
2632 {
2633   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2634 }
2635
2636 #if ENABLE_UNUSED_CODE
2637 static char *getPackedLevelFilename(int type)
2638 {
2639   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2640 }
2641 #endif
2642
2643 char *getDefaultLevelFilename(int nr)
2644 {
2645   return getSingleLevelFilename(nr);
2646 }
2647
2648 #if ENABLE_UNUSED_CODE
2649 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2650                                                  int type)
2651 {
2652   lfi->type = type;
2653   lfi->packed = FALSE;
2654
2655   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2656   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2657 }
2658 #endif
2659
2660 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2661                                                  int type, char *format, ...)
2662 {
2663   static char basename[MAX_FILENAME_LEN];
2664   va_list ap;
2665
2666   va_start(ap, format);
2667   vsprintf(basename, format, ap);
2668   va_end(ap);
2669
2670   lfi->type = type;
2671   lfi->packed = FALSE;
2672
2673   setString(&lfi->basename, basename);
2674   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2675 }
2676
2677 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2678                                                  int type)
2679 {
2680   lfi->type = type;
2681   lfi->packed = TRUE;
2682
2683   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2684   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2685 }
2686
2687 static int getFiletypeFromID(char *filetype_id)
2688 {
2689   char *filetype_id_lower;
2690   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2691   int i;
2692
2693   if (filetype_id == NULL)
2694     return LEVEL_FILE_TYPE_UNKNOWN;
2695
2696   filetype_id_lower = getStringToLower(filetype_id);
2697
2698   for (i = 0; filetype_id_list[i].id != NULL; i++)
2699   {
2700     char *id_lower = getStringToLower(filetype_id_list[i].id);
2701     
2702     if (strEqual(filetype_id_lower, id_lower))
2703       filetype = filetype_id_list[i].filetype;
2704
2705     free(id_lower);
2706
2707     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2708       break;
2709   }
2710
2711   free(filetype_id_lower);
2712
2713   return filetype;
2714 }
2715
2716 char *getLocalLevelTemplateFilename(void)
2717 {
2718   return getDefaultLevelFilename(-1);
2719 }
2720
2721 char *getGlobalLevelTemplateFilename(void)
2722 {
2723   // global variable "leveldir_current" must be modified in the loop below
2724   LevelDirTree *leveldir_current_last = leveldir_current;
2725   char *filename = NULL;
2726
2727   // check for template level in path from current to topmost tree node
2728
2729   while (leveldir_current != NULL)
2730   {
2731     filename = getDefaultLevelFilename(-1);
2732
2733     if (fileExists(filename))
2734       break;
2735
2736     leveldir_current = leveldir_current->node_parent;
2737   }
2738
2739   // restore global variable "leveldir_current" modified in above loop
2740   leveldir_current = leveldir_current_last;
2741
2742   return filename;
2743 }
2744
2745 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2746 {
2747   int nr = lfi->nr;
2748
2749   // special case: level number is negative => check for level template file
2750   if (nr < 0)
2751   {
2752     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2753                                          getSingleLevelBasename(-1));
2754
2755     // replace local level template filename with global template filename
2756     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2757
2758     // no fallback if template file not existing
2759     return;
2760   }
2761
2762   // special case: check for file name/pattern specified in "levelinfo.conf"
2763   if (leveldir_current->level_filename != NULL)
2764   {
2765     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2766
2767     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2768                                          leveldir_current->level_filename, nr);
2769
2770     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2771
2772     if (fileExists(lfi->filename))
2773       return;
2774   }
2775   else if (leveldir_current->level_filetype != NULL)
2776   {
2777     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2778
2779     // check for specified native level file with standard file name
2780     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2781                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2782     if (fileExists(lfi->filename))
2783       return;
2784   }
2785
2786   // check for native Rocks'n'Diamonds level file
2787   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2788                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2789   if (fileExists(lfi->filename))
2790     return;
2791
2792   // check for native Boulder Dash level file
2793   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2794   if (fileExists(lfi->filename))
2795     return;
2796
2797   // check for Emerald Mine level file (V1)
2798   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2799                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2800   if (fileExists(lfi->filename))
2801     return;
2802   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2803                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2804   if (fileExists(lfi->filename))
2805     return;
2806
2807   // check for Emerald Mine level file (V2 to V5)
2808   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2809   if (fileExists(lfi->filename))
2810     return;
2811
2812   // check for Emerald Mine level file (V6 / single mode)
2813   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2814   if (fileExists(lfi->filename))
2815     return;
2816   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2817   if (fileExists(lfi->filename))
2818     return;
2819
2820   // check for Emerald Mine level file (V6 / teamwork mode)
2821   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2822   if (fileExists(lfi->filename))
2823     return;
2824   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2825   if (fileExists(lfi->filename))
2826     return;
2827
2828   // check for various packed level file formats
2829   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2830   if (fileExists(lfi->filename))
2831     return;
2832
2833   // no known level file found -- use default values (and fail later)
2834   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2835                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2836 }
2837
2838 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2839 {
2840   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2841     lfi->type = getFileTypeFromBasename(lfi->basename);
2842
2843   if (lfi->type == LEVEL_FILE_TYPE_RND)
2844     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2845 }
2846
2847 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2848 {
2849   // always start with reliable default values
2850   setFileInfoToDefaults(level_file_info);
2851
2852   level_file_info->nr = nr;     // set requested level number
2853
2854   determineLevelFileInfo_Filename(level_file_info);
2855   determineLevelFileInfo_Filetype(level_file_info);
2856 }
2857
2858 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2859                               struct LevelFileInfo *lfi_to)
2860 {
2861   lfi_to->nr     = lfi_from->nr;
2862   lfi_to->type   = lfi_from->type;
2863   lfi_to->packed = lfi_from->packed;
2864
2865   setString(&lfi_to->basename, lfi_from->basename);
2866   setString(&lfi_to->filename, lfi_from->filename);
2867 }
2868
2869 // ----------------------------------------------------------------------------
2870 // functions for loading R'n'D level
2871 // ----------------------------------------------------------------------------
2872
2873 int getMappedElement(int element)
2874 {
2875   // remap some (historic, now obsolete) elements
2876
2877   switch (element)
2878   {
2879     case EL_PLAYER_OBSOLETE:
2880       element = EL_PLAYER_1;
2881       break;
2882
2883     case EL_KEY_OBSOLETE:
2884       element = EL_KEY_1;
2885       break;
2886
2887     case EL_EM_KEY_1_FILE_OBSOLETE:
2888       element = EL_EM_KEY_1;
2889       break;
2890
2891     case EL_EM_KEY_2_FILE_OBSOLETE:
2892       element = EL_EM_KEY_2;
2893       break;
2894
2895     case EL_EM_KEY_3_FILE_OBSOLETE:
2896       element = EL_EM_KEY_3;
2897       break;
2898
2899     case EL_EM_KEY_4_FILE_OBSOLETE:
2900       element = EL_EM_KEY_4;
2901       break;
2902
2903     case EL_ENVELOPE_OBSOLETE:
2904       element = EL_ENVELOPE_1;
2905       break;
2906
2907     case EL_SP_EMPTY:
2908       element = EL_EMPTY;
2909       break;
2910
2911     default:
2912       if (element >= NUM_FILE_ELEMENTS)
2913       {
2914         Warn("invalid level element %d", element);
2915
2916         element = EL_UNKNOWN;
2917       }
2918       break;
2919   }
2920
2921   return element;
2922 }
2923
2924 static int getMappedElementByVersion(int element, int game_version)
2925 {
2926   // remap some elements due to certain game version
2927
2928   if (game_version <= VERSION_IDENT(2,2,0,0))
2929   {
2930     // map game font elements
2931     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2932                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2933                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2934                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2935   }
2936
2937   if (game_version < VERSION_IDENT(3,0,0,0))
2938   {
2939     // map Supaplex gravity tube elements
2940     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2941                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2942                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2943                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2944                element);
2945   }
2946
2947   return element;
2948 }
2949
2950 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2951 {
2952   level->file_version = getFileVersion(file);
2953   level->game_version = getFileVersion(file);
2954
2955   return chunk_size;
2956 }
2957
2958 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2959 {
2960   level->creation_date.year  = getFile16BitBE(file);
2961   level->creation_date.month = getFile8Bit(file);
2962   level->creation_date.day   = getFile8Bit(file);
2963
2964   level->creation_date.src   = DATE_SRC_LEVELFILE;
2965
2966   return chunk_size;
2967 }
2968
2969 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2970 {
2971   int initial_player_stepsize;
2972   int initial_player_gravity;
2973   int i, x, y;
2974
2975   level->fieldx = getFile8Bit(file);
2976   level->fieldy = getFile8Bit(file);
2977
2978   level->time           = getFile16BitBE(file);
2979   level->gems_needed    = getFile16BitBE(file);
2980
2981   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2982     level->name[i] = getFile8Bit(file);
2983   level->name[MAX_LEVEL_NAME_LEN] = 0;
2984
2985   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2986     level->score[i] = getFile8Bit(file);
2987
2988   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2989   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2990     for (y = 0; y < 3; y++)
2991       for (x = 0; x < 3; x++)
2992         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2993
2994   level->amoeba_speed           = getFile8Bit(file);
2995   level->time_magic_wall        = getFile8Bit(file);
2996   level->time_wheel             = getFile8Bit(file);
2997   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2998
2999   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3000                                    STEPSIZE_NORMAL);
3001
3002   for (i = 0; i < MAX_PLAYERS; i++)
3003     level->initial_player_stepsize[i] = initial_player_stepsize;
3004
3005   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3006
3007   for (i = 0; i < MAX_PLAYERS; i++)
3008     level->initial_player_gravity[i] = initial_player_gravity;
3009
3010   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3012
3013   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3014
3015   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3017   level->can_move_into_acid_bits = getFile32BitBE(file);
3018   level->dont_collide_with_bits = getFile8Bit(file);
3019
3020   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3022
3023   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3024   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3025   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3026
3027   level->game_engine_type       = getFile8Bit(file);
3028
3029   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3030
3031   return chunk_size;
3032 }
3033
3034 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3035 {
3036   int i;
3037
3038   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3039     level->name[i] = getFile8Bit(file);
3040   level->name[MAX_LEVEL_NAME_LEN] = 0;
3041
3042   return chunk_size;
3043 }
3044
3045 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3046 {
3047   int i;
3048
3049   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3050     level->author[i] = getFile8Bit(file);
3051   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3052
3053   return chunk_size;
3054 }
3055
3056 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3057 {
3058   int x, y;
3059   int chunk_size_expected = level->fieldx * level->fieldy;
3060
3061   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3062      stored with 16-bit encoding (and should be twice as big then).
3063      Even worse, playfield data was stored 16-bit when only yamyam content
3064      contained 16-bit elements and vice versa. */
3065
3066   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3067     chunk_size_expected *= 2;
3068
3069   if (chunk_size_expected != chunk_size)
3070   {
3071     ReadUnusedBytesFromFile(file, chunk_size);
3072     return chunk_size_expected;
3073   }
3074
3075   for (y = 0; y < level->fieldy; y++)
3076     for (x = 0; x < level->fieldx; x++)
3077       level->field[x][y] =
3078         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3079                          getFile8Bit(file));
3080   return chunk_size;
3081 }
3082
3083 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3084 {
3085   int i, x, y;
3086   int header_size = 4;
3087   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3088   int chunk_size_expected = header_size + content_size;
3089
3090   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3091      stored with 16-bit encoding (and should be twice as big then).
3092      Even worse, playfield data was stored 16-bit when only yamyam content
3093      contained 16-bit elements and vice versa. */
3094
3095   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3096     chunk_size_expected += content_size;
3097
3098   if (chunk_size_expected != chunk_size)
3099   {
3100     ReadUnusedBytesFromFile(file, chunk_size);
3101     return chunk_size_expected;
3102   }
3103
3104   getFile8Bit(file);
3105   level->num_yamyam_contents = getFile8Bit(file);
3106   getFile8Bit(file);
3107   getFile8Bit(file);
3108
3109   // correct invalid number of content fields -- should never happen
3110   if (level->num_yamyam_contents < 1 ||
3111       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3112     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3113
3114   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3115     for (y = 0; y < 3; y++)
3116       for (x = 0; x < 3; x++)
3117         level->yamyam_content[i].e[x][y] =
3118           getMappedElement(level->encoding_16bit_field ?
3119                            getFile16BitBE(file) : getFile8Bit(file));
3120   return chunk_size;
3121 }
3122
3123 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3124 {
3125   int i, x, y;
3126   int element;
3127   int num_contents;
3128   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3129
3130   element = getMappedElement(getFile16BitBE(file));
3131   num_contents = getFile8Bit(file);
3132
3133   getFile8Bit(file);    // content x size (unused)
3134   getFile8Bit(file);    // content y size (unused)
3135
3136   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3137
3138   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3139     for (y = 0; y < 3; y++)
3140       for (x = 0; x < 3; x++)
3141         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3142
3143   // correct invalid number of content fields -- should never happen
3144   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3145     num_contents = STD_ELEMENT_CONTENTS;
3146
3147   if (element == EL_YAMYAM)
3148   {
3149     level->num_yamyam_contents = num_contents;
3150
3151     for (i = 0; i < num_contents; i++)
3152       for (y = 0; y < 3; y++)
3153         for (x = 0; x < 3; x++)
3154           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3155   }
3156   else if (element == EL_BD_AMOEBA)
3157   {
3158     level->amoeba_content = content_array[0][0][0];
3159   }
3160   else
3161   {
3162     Warn("cannot load content for element '%d'", element);
3163   }
3164
3165   return chunk_size;
3166 }
3167
3168 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3169 {
3170   int i;
3171   int element;
3172   int envelope_nr;
3173   int envelope_len;
3174   int chunk_size_expected;
3175
3176   element = getMappedElement(getFile16BitBE(file));
3177   if (!IS_ENVELOPE(element))
3178     element = EL_ENVELOPE_1;
3179
3180   envelope_nr = element - EL_ENVELOPE_1;
3181
3182   envelope_len = getFile16BitBE(file);
3183
3184   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3185   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3186
3187   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3188
3189   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3190   if (chunk_size_expected != chunk_size)
3191   {
3192     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3193     return chunk_size_expected;
3194   }
3195
3196   for (i = 0; i < envelope_len; i++)
3197     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3198
3199   return chunk_size;
3200 }
3201
3202 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3203 {
3204   int num_changed_custom_elements = getFile16BitBE(file);
3205   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3206   int i;
3207
3208   if (chunk_size_expected != chunk_size)
3209   {
3210     ReadUnusedBytesFromFile(file, chunk_size - 2);
3211     return chunk_size_expected;
3212   }
3213
3214   for (i = 0; i < num_changed_custom_elements; i++)
3215   {
3216     int element = getMappedElement(getFile16BitBE(file));
3217     int properties = getFile32BitBE(file);
3218
3219     if (IS_CUSTOM_ELEMENT(element))
3220       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3221     else
3222       Warn("invalid custom element number %d", element);
3223
3224     // older game versions that wrote level files with CUS1 chunks used
3225     // different default push delay values (not yet stored in level file)
3226     element_info[element].push_delay_fixed = 2;
3227     element_info[element].push_delay_random = 8;
3228   }
3229
3230   level->file_has_custom_elements = TRUE;
3231
3232   return chunk_size;
3233 }
3234
3235 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3236 {
3237   int num_changed_custom_elements = getFile16BitBE(file);
3238   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3239   int i;
3240
3241   if (chunk_size_expected != chunk_size)
3242   {
3243     ReadUnusedBytesFromFile(file, chunk_size - 2);
3244     return chunk_size_expected;
3245   }
3246
3247   for (i = 0; i < num_changed_custom_elements; i++)
3248   {
3249     int element = getMappedElement(getFile16BitBE(file));
3250     int custom_target_element = getMappedElement(getFile16BitBE(file));
3251
3252     if (IS_CUSTOM_ELEMENT(element))
3253       element_info[element].change->target_element = custom_target_element;
3254     else
3255       Warn("invalid custom element number %d", element);
3256   }
3257
3258   level->file_has_custom_elements = TRUE;
3259
3260   return chunk_size;
3261 }
3262
3263 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3264 {
3265   int num_changed_custom_elements = getFile16BitBE(file);
3266   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3267   int i, j, x, y;
3268
3269   if (chunk_size_expected != chunk_size)
3270   {
3271     ReadUnusedBytesFromFile(file, chunk_size - 2);
3272     return chunk_size_expected;
3273   }
3274
3275   for (i = 0; i < num_changed_custom_elements; i++)
3276   {
3277     int element = getMappedElement(getFile16BitBE(file));
3278     struct ElementInfo *ei = &element_info[element];
3279     unsigned int event_bits;
3280
3281     if (!IS_CUSTOM_ELEMENT(element))
3282     {
3283       Warn("invalid custom element number %d", element);
3284
3285       element = EL_INTERNAL_DUMMY;
3286     }
3287
3288     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3289       ei->description[j] = getFile8Bit(file);
3290     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3291
3292     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3293
3294     // some free bytes for future properties and padding
3295     ReadUnusedBytesFromFile(file, 7);
3296
3297     ei->use_gfx_element = getFile8Bit(file);
3298     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3299
3300     ei->collect_score_initial = getFile8Bit(file);
3301     ei->collect_count_initial = getFile8Bit(file);
3302
3303     ei->push_delay_fixed = getFile16BitBE(file);
3304     ei->push_delay_random = getFile16BitBE(file);
3305     ei->move_delay_fixed = getFile16BitBE(file);
3306     ei->move_delay_random = getFile16BitBE(file);
3307
3308     ei->move_pattern = getFile16BitBE(file);
3309     ei->move_direction_initial = getFile8Bit(file);
3310     ei->move_stepsize = getFile8Bit(file);
3311
3312     for (y = 0; y < 3; y++)
3313       for (x = 0; x < 3; x++)
3314         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3315
3316     // bits 0 - 31 of "has_event[]"
3317     event_bits = getFile32BitBE(file);
3318     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3319       if (event_bits & (1u << j))
3320         ei->change->has_event[j] = TRUE;
3321
3322     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3323
3324     ei->change->delay_fixed = getFile16BitBE(file);
3325     ei->change->delay_random = getFile16BitBE(file);
3326     ei->change->delay_frames = getFile16BitBE(file);
3327
3328     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3329
3330     ei->change->explode = getFile8Bit(file);
3331     ei->change->use_target_content = getFile8Bit(file);
3332     ei->change->only_if_complete = getFile8Bit(file);
3333     ei->change->use_random_replace = getFile8Bit(file);
3334
3335     ei->change->random_percentage = getFile8Bit(file);
3336     ei->change->replace_when = getFile8Bit(file);
3337
3338     for (y = 0; y < 3; y++)
3339       for (x = 0; x < 3; x++)
3340         ei->change->target_content.e[x][y] =
3341           getMappedElement(getFile16BitBE(file));
3342
3343     ei->slippery_type = getFile8Bit(file);
3344
3345     // some free bytes for future properties and padding
3346     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3347
3348     // mark that this custom element has been modified
3349     ei->modified_settings = TRUE;
3350   }
3351
3352   level->file_has_custom_elements = TRUE;
3353
3354   return chunk_size;
3355 }
3356
3357 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3358 {
3359   struct ElementInfo *ei;
3360   int chunk_size_expected;
3361   int element;
3362   int i, j, x, y;
3363
3364   // ---------- custom element base property values (96 bytes) ----------------
3365
3366   element = getMappedElement(getFile16BitBE(file));
3367
3368   if (!IS_CUSTOM_ELEMENT(element))
3369   {
3370     Warn("invalid custom element number %d", element);
3371
3372     ReadUnusedBytesFromFile(file, chunk_size - 2);
3373
3374     return chunk_size;
3375   }
3376
3377   ei = &element_info[element];
3378
3379   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3380     ei->description[i] = getFile8Bit(file);
3381   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3382
3383   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3384
3385   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3386
3387   ei->num_change_pages = getFile8Bit(file);
3388
3389   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3390   if (chunk_size_expected != chunk_size)
3391   {
3392     ReadUnusedBytesFromFile(file, chunk_size - 43);
3393     return chunk_size_expected;
3394   }
3395
3396   ei->ce_value_fixed_initial = getFile16BitBE(file);
3397   ei->ce_value_random_initial = getFile16BitBE(file);
3398   ei->use_last_ce_value = getFile8Bit(file);
3399
3400   ei->use_gfx_element = getFile8Bit(file);
3401   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3402
3403   ei->collect_score_initial = getFile8Bit(file);
3404   ei->collect_count_initial = getFile8Bit(file);
3405
3406   ei->drop_delay_fixed = getFile8Bit(file);
3407   ei->push_delay_fixed = getFile8Bit(file);
3408   ei->drop_delay_random = getFile8Bit(file);
3409   ei->push_delay_random = getFile8Bit(file);
3410   ei->move_delay_fixed = getFile16BitBE(file);
3411   ei->move_delay_random = getFile16BitBE(file);
3412
3413   // bits 0 - 15 of "move_pattern" ...
3414   ei->move_pattern = getFile16BitBE(file);
3415   ei->move_direction_initial = getFile8Bit(file);
3416   ei->move_stepsize = getFile8Bit(file);
3417
3418   ei->slippery_type = getFile8Bit(file);
3419
3420   for (y = 0; y < 3; y++)
3421     for (x = 0; x < 3; x++)
3422       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3423
3424   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3425   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3426   ei->move_leave_type = getFile8Bit(file);
3427
3428   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3429   ei->move_pattern |= (getFile16BitBE(file) << 16);
3430
3431   ei->access_direction = getFile8Bit(file);
3432
3433   ei->explosion_delay = getFile8Bit(file);
3434   ei->ignition_delay = getFile8Bit(file);
3435   ei->explosion_type = getFile8Bit(file);
3436
3437   // some free bytes for future custom property values and padding
3438   ReadUnusedBytesFromFile(file, 1);
3439
3440   // ---------- change page property values (48 bytes) ------------------------
3441
3442   setElementChangePages(ei, ei->num_change_pages);
3443
3444   for (i = 0; i < ei->num_change_pages; i++)
3445   {
3446     struct ElementChangeInfo *change = &ei->change_page[i];
3447     unsigned int event_bits;
3448
3449     // always start with reliable default values
3450     setElementChangeInfoToDefaults(change);
3451
3452     // bits 0 - 31 of "has_event[]" ...
3453     event_bits = getFile32BitBE(file);
3454     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3455       if (event_bits & (1u << j))
3456         change->has_event[j] = TRUE;
3457
3458     change->target_element = getMappedElement(getFile16BitBE(file));
3459
3460     change->delay_fixed = getFile16BitBE(file);
3461     change->delay_random = getFile16BitBE(file);
3462     change->delay_frames = getFile16BitBE(file);
3463
3464     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3465
3466     change->explode = getFile8Bit(file);
3467     change->use_target_content = getFile8Bit(file);
3468     change->only_if_complete = getFile8Bit(file);
3469     change->use_random_replace = getFile8Bit(file);
3470
3471     change->random_percentage = getFile8Bit(file);
3472     change->replace_when = getFile8Bit(file);
3473
3474     for (y = 0; y < 3; y++)
3475       for (x = 0; x < 3; x++)
3476         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3477
3478     change->can_change = getFile8Bit(file);
3479
3480     change->trigger_side = getFile8Bit(file);
3481
3482     change->trigger_player = getFile8Bit(file);
3483     change->trigger_page = getFile8Bit(file);
3484
3485     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3486                             CH_PAGE_ANY : (1 << change->trigger_page));
3487
3488     change->has_action = getFile8Bit(file);
3489     change->action_type = getFile8Bit(file);
3490     change->action_mode = getFile8Bit(file);
3491     change->action_arg = getFile16BitBE(file);
3492
3493     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3494     event_bits = getFile8Bit(file);
3495     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3496       if (event_bits & (1u << (j - 32)))
3497         change->has_event[j] = TRUE;
3498   }
3499
3500   // mark this custom element as modified
3501   ei->modified_settings = TRUE;
3502
3503   level->file_has_custom_elements = TRUE;
3504
3505   return chunk_size;
3506 }
3507
3508 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3509 {
3510   struct ElementInfo *ei;
3511   struct ElementGroupInfo *group;
3512   int element;
3513   int i;
3514
3515   element = getMappedElement(getFile16BitBE(file));
3516
3517   if (!IS_GROUP_ELEMENT(element))
3518   {
3519     Warn("invalid group element number %d", element);
3520
3521     ReadUnusedBytesFromFile(file, chunk_size - 2);
3522
3523     return chunk_size;
3524   }
3525
3526   ei = &element_info[element];
3527
3528   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3529     ei->description[i] = getFile8Bit(file);
3530   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3531
3532   group = element_info[element].group;
3533
3534   group->num_elements = getFile8Bit(file);
3535
3536   ei->use_gfx_element = getFile8Bit(file);
3537   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3538
3539   group->choice_mode = getFile8Bit(file);
3540
3541   // some free bytes for future values and padding
3542   ReadUnusedBytesFromFile(file, 3);
3543
3544   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3545     group->element[i] = getMappedElement(getFile16BitBE(file));
3546
3547   // mark this group element as modified
3548   element_info[element].modified_settings = TRUE;
3549
3550   level->file_has_custom_elements = TRUE;
3551
3552   return chunk_size;
3553 }
3554
3555 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3556                                 int element, int real_element)
3557 {
3558   int micro_chunk_size = 0;
3559   int conf_type = getFile8Bit(file);
3560   int byte_mask = conf_type & CONF_MASK_BYTES;
3561   boolean element_found = FALSE;
3562   int i;
3563
3564   micro_chunk_size += 1;
3565
3566   if (byte_mask == CONF_MASK_MULTI_BYTES)
3567   {
3568     int num_bytes = getFile16BitBE(file);
3569     byte *buffer = checked_malloc(num_bytes);
3570
3571     ReadBytesFromFile(file, buffer, num_bytes);
3572
3573     for (i = 0; conf[i].data_type != -1; i++)
3574     {
3575       if (conf[i].element == element &&
3576           conf[i].conf_type == conf_type)
3577       {
3578         int data_type = conf[i].data_type;
3579         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3580         int max_num_entities = conf[i].max_num_entities;
3581
3582         if (num_entities > max_num_entities)
3583         {
3584           Warn("truncating number of entities for element %d from %d to %d",
3585                element, num_entities, max_num_entities);
3586
3587           num_entities = max_num_entities;
3588         }
3589
3590         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3591                                   data_type == TYPE_CONTENT_LIST))
3592         {
3593           // for element and content lists, zero entities are not allowed
3594           Warn("found empty list of entities for element %d", element);
3595
3596           // do not set "num_entities" here to prevent reading behind buffer
3597
3598           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3599         }
3600         else
3601         {
3602           *(int *)(conf[i].num_entities) = num_entities;
3603         }
3604
3605         element_found = TRUE;
3606
3607         if (data_type == TYPE_STRING)
3608         {
3609           char *string = (char *)(conf[i].value);
3610           int j;
3611
3612           for (j = 0; j < max_num_entities; j++)
3613             string[j] = (j < num_entities ? buffer[j] : '\0');
3614         }
3615         else if (data_type == TYPE_ELEMENT_LIST)
3616         {
3617           int *element_array = (int *)(conf[i].value);
3618           int j;
3619
3620           for (j = 0; j < num_entities; j++)
3621             element_array[j] =
3622               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3623         }
3624         else if (data_type == TYPE_CONTENT_LIST)
3625         {
3626           struct Content *content= (struct Content *)(conf[i].value);
3627           int c, x, y;
3628
3629           for (c = 0; c < num_entities; c++)
3630             for (y = 0; y < 3; y++)
3631               for (x = 0; x < 3; x++)
3632                 content[c].e[x][y] =
3633                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3634         }
3635         else
3636           element_found = FALSE;
3637
3638         break;
3639       }
3640     }
3641
3642     checked_free(buffer);
3643
3644     micro_chunk_size += 2 + num_bytes;
3645   }
3646   else          // constant size configuration data (1, 2 or 4 bytes)
3647   {
3648     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3649                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3650                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3651
3652     for (i = 0; conf[i].data_type != -1; i++)
3653     {
3654       if (conf[i].element == element &&
3655           conf[i].conf_type == conf_type)
3656       {
3657         int data_type = conf[i].data_type;
3658
3659         if (data_type == TYPE_ELEMENT)
3660           value = getMappedElement(value);
3661
3662         if (data_type == TYPE_BOOLEAN)
3663           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3664         else
3665           *(int *)    (conf[i].value) = value;
3666
3667         element_found = TRUE;
3668
3669         break;
3670       }
3671     }
3672
3673     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3674   }
3675
3676   if (!element_found)
3677   {
3678     char *error_conf_chunk_bytes =
3679       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3680        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3681        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3682     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3683     int error_element = real_element;
3684
3685     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3686          error_conf_chunk_bytes, error_conf_chunk_token,
3687          error_element, EL_NAME(error_element));
3688   }
3689
3690   return micro_chunk_size;
3691 }
3692
3693 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3694 {
3695   int real_chunk_size = 0;
3696
3697   li = *level;          // copy level data into temporary buffer
3698
3699   while (!checkEndOfFile(file))
3700   {
3701     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3702
3703     if (real_chunk_size >= chunk_size)
3704       break;
3705   }
3706
3707   *level = li;          // copy temporary buffer back to level data
3708
3709   return real_chunk_size;
3710 }
3711
3712 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3713 {
3714   int real_chunk_size = 0;
3715
3716   li = *level;          // copy level data into temporary buffer
3717
3718   while (!checkEndOfFile(file))
3719   {
3720     int element = getMappedElement(getFile16BitBE(file));
3721
3722     real_chunk_size += 2;
3723     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3724                                             element, element);
3725     if (real_chunk_size >= chunk_size)
3726       break;
3727   }
3728
3729   *level = li;          // copy temporary buffer back to level data
3730
3731   return real_chunk_size;
3732 }
3733
3734 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3735 {
3736   int real_chunk_size = 0;
3737
3738   li = *level;          // copy level data into temporary buffer
3739
3740   while (!checkEndOfFile(file))
3741   {
3742     int element = getMappedElement(getFile16BitBE(file));
3743
3744     real_chunk_size += 2;
3745     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3746                                             element, element);
3747     if (real_chunk_size >= chunk_size)
3748       break;
3749   }
3750
3751   *level = li;          // copy temporary buffer back to level data
3752
3753   return real_chunk_size;
3754 }
3755
3756 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3757 {
3758   int element = getMappedElement(getFile16BitBE(file));
3759   int envelope_nr = element - EL_ENVELOPE_1;
3760   int real_chunk_size = 2;
3761
3762   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3763
3764   while (!checkEndOfFile(file))
3765   {
3766     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3767                                             -1, element);
3768
3769     if (real_chunk_size >= chunk_size)
3770       break;
3771   }
3772
3773   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3774
3775   return real_chunk_size;
3776 }
3777
3778 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3779 {
3780   int element = getMappedElement(getFile16BitBE(file));
3781   int real_chunk_size = 2;
3782   struct ElementInfo *ei = &element_info[element];
3783   int i;
3784
3785   xx_ei = *ei;          // copy element data into temporary buffer
3786
3787   xx_ei.num_change_pages = -1;
3788
3789   while (!checkEndOfFile(file))
3790   {
3791     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3792                                             -1, element);
3793     if (xx_ei.num_change_pages != -1)
3794       break;
3795
3796     if (real_chunk_size >= chunk_size)
3797       break;
3798   }
3799
3800   *ei = xx_ei;
3801
3802   if (ei->num_change_pages == -1)
3803   {
3804     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3805          EL_NAME(element));
3806
3807     ei->num_change_pages = 1;
3808
3809     setElementChangePages(ei, 1);
3810     setElementChangeInfoToDefaults(ei->change);
3811
3812     return real_chunk_size;
3813   }
3814
3815   // initialize number of change pages stored for this custom element
3816   setElementChangePages(ei, ei->num_change_pages);
3817   for (i = 0; i < ei->num_change_pages; i++)
3818     setElementChangeInfoToDefaults(&ei->change_page[i]);
3819
3820   // start with reading properties for the first change page
3821   xx_current_change_page = 0;
3822
3823   while (!checkEndOfFile(file))
3824   {
3825     // level file might contain invalid change page number
3826     if (xx_current_change_page >= ei->num_change_pages)
3827       break;
3828
3829     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3830
3831     xx_change = *change;        // copy change data into temporary buffer
3832
3833     resetEventBits();           // reset bits; change page might have changed
3834
3835     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3836                                             -1, element);
3837
3838     *change = xx_change;
3839
3840     setEventFlagsFromEventBits(change);
3841
3842     if (real_chunk_size >= chunk_size)
3843       break;
3844   }
3845
3846   level->file_has_custom_elements = TRUE;
3847
3848   return real_chunk_size;
3849 }
3850
3851 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3852 {
3853   int element = getMappedElement(getFile16BitBE(file));
3854   int real_chunk_size = 2;
3855   struct ElementInfo *ei = &element_info[element];
3856   struct ElementGroupInfo *group = ei->group;
3857
3858   if (group == NULL)
3859     return -1;
3860
3861   xx_ei = *ei;          // copy element data into temporary buffer
3862   xx_group = *group;    // copy group data into temporary buffer
3863
3864   while (!checkEndOfFile(file))
3865   {
3866     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3867                                             -1, element);
3868
3869     if (real_chunk_size >= chunk_size)
3870       break;
3871   }
3872
3873   *ei = xx_ei;
3874   *group = xx_group;
3875
3876   level->file_has_custom_elements = TRUE;
3877
3878   return real_chunk_size;
3879 }
3880
3881 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3882 {
3883   int element = getMappedElement(getFile16BitBE(file));
3884   int real_chunk_size = 2;
3885   struct ElementInfo *ei = &element_info[element];
3886
3887   xx_ei = *ei;          // copy element data into temporary buffer
3888
3889   while (!checkEndOfFile(file))
3890   {
3891     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3892                                             -1, element);
3893
3894     if (real_chunk_size >= chunk_size)
3895       break;
3896   }
3897
3898   *ei = xx_ei;
3899
3900   level->file_has_custom_elements = TRUE;
3901
3902   return real_chunk_size;
3903 }
3904
3905 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3906                                       struct LevelFileInfo *level_file_info,
3907                                       boolean level_info_only)
3908 {
3909   char *filename = level_file_info->filename;
3910   char cookie[MAX_LINE_LEN];
3911   char chunk_name[CHUNK_ID_LEN + 1];
3912   int chunk_size;
3913   File *file;
3914
3915   if (!(file = openFile(filename, MODE_READ)))
3916   {
3917     level->no_valid_file = TRUE;
3918     level->no_level_file = TRUE;
3919
3920     if (level_info_only)
3921       return;
3922
3923     Warn("cannot read level '%s' -- using empty level", filename);
3924
3925     if (!setup.editor.use_template_for_new_levels)
3926       return;
3927
3928     // if level file not found, try to initialize level data from template
3929     filename = getGlobalLevelTemplateFilename();
3930
3931     if (!(file = openFile(filename, MODE_READ)))
3932       return;
3933
3934     // default: for empty levels, use level template for custom elements
3935     level->use_custom_template = TRUE;
3936
3937     level->no_valid_file = FALSE;
3938   }
3939
3940   getFileChunkBE(file, chunk_name, NULL);
3941   if (strEqual(chunk_name, "RND1"))
3942   {
3943     getFile32BitBE(file);               // not used
3944
3945     getFileChunkBE(file, chunk_name, NULL);
3946     if (!strEqual(chunk_name, "CAVE"))
3947     {
3948       level->no_valid_file = TRUE;
3949
3950       Warn("unknown format of level file '%s'", filename);
3951
3952       closeFile(file);
3953
3954       return;
3955     }
3956   }
3957   else  // check for pre-2.0 file format with cookie string
3958   {
3959     strcpy(cookie, chunk_name);
3960     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3961       cookie[4] = '\0';
3962     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3963       cookie[strlen(cookie) - 1] = '\0';
3964
3965     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3966     {
3967       level->no_valid_file = TRUE;
3968
3969       Warn("unknown format of level file '%s'", filename);
3970
3971       closeFile(file);
3972
3973       return;
3974     }
3975
3976     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3977     {
3978       level->no_valid_file = TRUE;
3979
3980       Warn("unsupported version of level file '%s'", filename);
3981
3982       closeFile(file);
3983
3984       return;
3985     }
3986
3987     // pre-2.0 level files have no game version, so use file version here
3988     level->game_version = level->file_version;
3989   }
3990
3991   if (level->file_version < FILE_VERSION_1_2)
3992   {
3993     // level files from versions before 1.2.0 without chunk structure
3994     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3995     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3996   }
3997   else
3998   {
3999     static struct
4000     {
4001       char *name;
4002       int size;
4003       int (*loader)(File *, int, struct LevelInfo *);
4004     }
4005     chunk_info[] =
4006     {
4007       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4008       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4009       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4010       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4011       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4012       { "INFO", -1,                     LoadLevel_INFO },
4013       { "BODY", -1,                     LoadLevel_BODY },
4014       { "CONT", -1,                     LoadLevel_CONT },
4015       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4016       { "CNT3", -1,                     LoadLevel_CNT3 },
4017       { "CUS1", -1,                     LoadLevel_CUS1 },
4018       { "CUS2", -1,                     LoadLevel_CUS2 },
4019       { "CUS3", -1,                     LoadLevel_CUS3 },
4020       { "CUS4", -1,                     LoadLevel_CUS4 },
4021       { "GRP1", -1,                     LoadLevel_GRP1 },
4022       { "CONF", -1,                     LoadLevel_CONF },
4023       { "ELEM", -1,                     LoadLevel_ELEM },
4024       { "NOTE", -1,                     LoadLevel_NOTE },
4025       { "CUSX", -1,                     LoadLevel_CUSX },
4026       { "GRPX", -1,                     LoadLevel_GRPX },
4027       { "EMPX", -1,                     LoadLevel_EMPX },
4028
4029       {  NULL,  0,                      NULL }
4030     };
4031
4032     while (getFileChunkBE(file, chunk_name, &chunk_size))
4033     {
4034       int i = 0;
4035
4036       while (chunk_info[i].name != NULL &&
4037              !strEqual(chunk_name, chunk_info[i].name))
4038         i++;
4039
4040       if (chunk_info[i].name == NULL)
4041       {
4042         Warn("unknown chunk '%s' in level file '%s'",
4043              chunk_name, filename);
4044
4045         ReadUnusedBytesFromFile(file, chunk_size);
4046       }
4047       else if (chunk_info[i].size != -1 &&
4048                chunk_info[i].size != chunk_size)
4049       {
4050         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4051              chunk_size, chunk_name, filename);
4052
4053         ReadUnusedBytesFromFile(file, chunk_size);
4054       }
4055       else
4056       {
4057         // call function to load this level chunk
4058         int chunk_size_expected =
4059           (chunk_info[i].loader)(file, chunk_size, level);
4060
4061         if (chunk_size_expected < 0)
4062         {
4063           Warn("error reading chunk '%s' in level file '%s'",
4064                chunk_name, filename);
4065
4066           break;
4067         }
4068
4069         // the size of some chunks cannot be checked before reading other
4070         // chunks first (like "HEAD" and "BODY") that contain some header
4071         // information, so check them here
4072         if (chunk_size_expected != chunk_size)
4073         {
4074           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4075                chunk_size, chunk_name, filename);
4076
4077           break;
4078         }
4079       }
4080     }
4081   }
4082
4083   closeFile(file);
4084 }
4085
4086
4087 // ----------------------------------------------------------------------------
4088 // functions for loading BD level
4089 // ----------------------------------------------------------------------------
4090
4091 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4092 {
4093   struct LevelInfo_BD *level_bd = level->native_bd_level;
4094   GdCave *cave = NULL;  // will be changed below
4095   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4096   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4097   int x, y;
4098
4099   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4100
4101   // cave and map newly allocated when set to defaults above
4102   cave = level_bd->cave;
4103
4104   // level type
4105   cave->intermission                    = level->bd_intermission;
4106
4107   // level settings
4108   cave->level_time[0]                   = level->time;
4109   cave->level_diamonds[0]               = level->gems_needed;
4110
4111   // game timing
4112   cave->scheduling                      = level->bd_scheduling_type;
4113   cave->pal_timing                      = level->bd_pal_timing;
4114   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4115   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4116   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4117   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4118
4119   // scores
4120   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4121   cave->diamond_value                   = level->score[SC_EMERALD];
4122   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4123
4124   // compatibility settings
4125   cave->lineshift                       = level->bd_line_shifting_borders;
4126   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4127   cave->short_explosions                = level->bd_short_explosions;
4128   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4129
4130   // player properties
4131   cave->diagonal_movements              = level->bd_diagonal_movements;
4132   cave->active_is_first_found           = level->bd_topmost_player_active;
4133   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4134   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4135   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4136   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4137
4138   // element properties
4139   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4140   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4141   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4142   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4143   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4144   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4145   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4146   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4147   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4148
4149   cave->magic_diamond_to                = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4150   cave->magic_stone_to                  = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4151   cave->magic_mega_stone_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4152   cave->magic_nut_to                    = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4153   cave->magic_nitro_pack_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4154   cave->magic_flying_diamond_to         = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4155   cave->magic_flying_stone_to           = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4156
4157   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4158   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4159   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4160   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4161   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4162   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4163   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4164   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4165   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4166   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4167   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4168
4169   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4170   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4171   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4172   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4173   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4174   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4175
4176   cave->slime_predictable               = level->bd_slime_is_predictable;
4177   cave->slime_correct_random            = level->bd_slime_correct_random;
4178   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4179   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4180   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4181   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4182   cave->slime_eats_1                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4183   cave->slime_converts_1                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4184   cave->slime_eats_2                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4185   cave->slime_converts_2                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4186   cave->slime_eats_3                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4187   cave->slime_converts_3                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4188
4189   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4190   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4191   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4192
4193   cave->biter_delay_frame               = level->bd_biter_move_delay;
4194   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4195
4196   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4197
4198   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4199
4200   cave->replicators_active              = level->bd_replicators_active;
4201   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4202
4203   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4204   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4205
4206   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4207
4208   cave->nut_turns_to_when_crushed       = map_element_RND_to_BD_cave(level->bd_nut_content);
4209
4210   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4211   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4212   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4213
4214   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4215   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4216
4217   cave->expanding_wall_looks_like       = map_element_RND_to_BD_cave(level->bd_expanding_wall_looks_like);
4218   cave->dirt_looks_like                 = map_element_RND_to_BD_cave(level->bd_sand_looks_like);
4219
4220   // level name
4221   strncpy(cave->name, level->name, sizeof(GdString));
4222   cave->name[sizeof(GdString) - 1] = '\0';
4223
4224   // playfield elements
4225   for (x = 0; x < cave->w; x++)
4226     for (y = 0; y < cave->h; y++)
4227       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4228 }
4229
4230 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4231 {
4232   struct LevelInfo_BD *level_bd = level->native_bd_level;
4233   GdCave *cave = level_bd->cave;
4234   int bd_level_nr = level_bd->level_nr;
4235   int x, y;
4236
4237   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4238   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4239
4240   // level type
4241   level->bd_intermission                = cave->intermission;
4242
4243   // level settings
4244   level->time                           = cave->level_time[bd_level_nr];
4245   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4246
4247   // game timing
4248   level->bd_scheduling_type             = cave->scheduling;
4249   level->bd_pal_timing                  = cave->pal_timing;
4250   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4251   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4252   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4253   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4254
4255   // scores
4256   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4257   level->score[SC_EMERALD]              = cave->diamond_value;
4258   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4259
4260   // compatibility settings
4261   level->bd_line_shifting_borders       = cave->lineshift;
4262   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4263   level->bd_short_explosions            = cave->short_explosions;
4264   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4265
4266   // player properties
4267   level->bd_diagonal_movements          = cave->diagonal_movements;
4268   level->bd_topmost_player_active       = cave->active_is_first_found;
4269   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4270   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4271   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4272   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4273
4274   // element properties
4275   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4276   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4277   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4278   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4279   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4280   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4281   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4282   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4283   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4284
4285   level->bd_magic_wall_diamond_to       = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4286   level->bd_magic_wall_rock_to          = map_element_BD_to_RND_cave(cave->magic_stone_to);
4287   level->bd_magic_wall_mega_rock_to     = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4288   level->bd_magic_wall_nut_to           = map_element_BD_to_RND_cave(cave->magic_nut_to);
4289   level->bd_magic_wall_nitro_pack_to    = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4290   level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4291   level->bd_magic_wall_flying_rock_to   = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4292
4293   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4294   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4295   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4296   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4297   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4298   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4299   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4300   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4301   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4302   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4303   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4304
4305   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4306   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4307   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4308   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4309   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4310   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4311
4312   level->bd_slime_is_predictable        = cave->slime_predictable;
4313   level->bd_slime_correct_random        = cave->slime_correct_random;
4314   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4315   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4316   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4317   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4318   level->bd_slime_eats_element_1        = map_element_BD_to_RND_cave(cave->slime_eats_1);
4319   level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4320   level->bd_slime_eats_element_2        = map_element_BD_to_RND_cave(cave->slime_eats_2);
4321   level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4322   level->bd_slime_eats_element_3        = map_element_BD_to_RND_cave(cave->slime_eats_3);
4323   level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4324
4325   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4326   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4327   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4328
4329   level->bd_biter_move_delay            = cave->biter_delay_frame;
4330   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4331
4332   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4333
4334   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4335
4336   level->bd_replicators_active          = cave->replicators_active;
4337   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4338
4339   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4340   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4341
4342   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4343
4344   level->bd_nut_content                 = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4345
4346   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4347   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4348   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4349
4350   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4351   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4352
4353   level->bd_expanding_wall_looks_like   = map_element_BD_to_RND_cave(cave->expanding_wall_looks_like);
4354   level->bd_sand_looks_like             = map_element_BD_to_RND_cave(cave->dirt_looks_like);
4355
4356   // level name
4357   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4358
4359   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4360   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4361
4362   // playfield elements
4363   for (x = 0; x < level->fieldx; x++)
4364     for (y = 0; y < level->fieldy; y++)
4365       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4366
4367   checked_free(cave_name);
4368 }
4369
4370 static void setTapeInfoToDefaults(void);
4371
4372 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4373 {
4374   struct LevelInfo_BD *level_bd = level->native_bd_level;
4375   GdCave *cave = level_bd->cave;
4376   GdReplay *replay = level_bd->replay;
4377   int i;
4378
4379   if (replay == NULL)
4380     return;
4381
4382   // always start with reliable default values
4383   setTapeInfoToDefaults();
4384
4385   tape.level_nr = level_nr;             // (currently not used)
4386   tape.random_seed = replay->seed;
4387
4388   TapeSetDateFromIsoDateString(replay->date);
4389
4390   tape.counter = 0;
4391   tape.pos[tape.counter].delay = 0;
4392
4393   tape.bd_replay = TRUE;
4394
4395   // all time calculations only used to display approximate tape time
4396   int cave_speed = cave->speed;
4397   int milliseconds_game = 0;
4398   int milliseconds_elapsed = 20;
4399
4400   for (i = 0; i < replay->movements->len; i++)
4401   {
4402     int replay_action = replay->movements->data[i];
4403     int tape_action = map_action_BD_to_RND(replay_action);
4404     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4405     boolean success = 0;
4406
4407     while (1)
4408     {
4409       success = TapeAddAction(action);
4410
4411       milliseconds_game += milliseconds_elapsed;
4412
4413       if (milliseconds_game >= cave_speed)
4414       {
4415         milliseconds_game -= cave_speed;
4416
4417         break;
4418       }
4419     }
4420
4421     tape.counter++;
4422     tape.pos[tape.counter].delay = 0;
4423     tape.pos[tape.counter].action[0] = 0;
4424
4425     if (!success)
4426     {
4427       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4428
4429       break;
4430     }
4431   }
4432
4433   TapeHaltRecording();
4434 }
4435
4436
4437 // ----------------------------------------------------------------------------
4438 // functions for loading EM level
4439 // ----------------------------------------------------------------------------
4440
4441 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4442 {
4443   static int ball_xy[8][2] =
4444   {
4445     { 0, 0 },
4446     { 1, 0 },
4447     { 2, 0 },
4448     { 0, 1 },
4449     { 2, 1 },
4450     { 0, 2 },
4451     { 1, 2 },
4452     { 2, 2 },
4453   };
4454   struct LevelInfo_EM *level_em = level->native_em_level;
4455   struct CAVE *cav = level_em->cav;
4456   int i, j, x, y;
4457
4458   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4459   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4460
4461   cav->time_seconds     = level->time;
4462   cav->gems_needed      = level->gems_needed;
4463
4464   cav->emerald_score    = level->score[SC_EMERALD];
4465   cav->diamond_score    = level->score[SC_DIAMOND];
4466   cav->alien_score      = level->score[SC_ROBOT];
4467   cav->tank_score       = level->score[SC_SPACESHIP];
4468   cav->bug_score        = level->score[SC_BUG];
4469   cav->eater_score      = level->score[SC_YAMYAM];
4470   cav->nut_score        = level->score[SC_NUT];
4471   cav->dynamite_score   = level->score[SC_DYNAMITE];
4472   cav->key_score        = level->score[SC_KEY];
4473   cav->exit_score       = level->score[SC_TIME_BONUS];
4474
4475   cav->num_eater_arrays = level->num_yamyam_contents;
4476
4477   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4478     for (y = 0; y < 3; y++)
4479       for (x = 0; x < 3; x++)
4480         cav->eater_array[i][y * 3 + x] =
4481           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4482
4483   cav->amoeba_time              = level->amoeba_speed;
4484   cav->wonderwall_time          = level->time_magic_wall;
4485   cav->wheel_time               = level->time_wheel;
4486
4487   cav->android_move_time        = level->android_move_time;
4488   cav->android_clone_time       = level->android_clone_time;
4489   cav->ball_random              = level->ball_random;
4490   cav->ball_active              = level->ball_active_initial;
4491   cav->ball_time                = level->ball_time;
4492   cav->num_ball_arrays          = level->num_ball_contents;
4493
4494   cav->lenses_score             = level->lenses_score;
4495   cav->magnify_score            = level->magnify_score;
4496   cav->slurp_score              = level->slurp_score;
4497
4498   cav->lenses_time              = level->lenses_time;
4499   cav->magnify_time             = level->magnify_time;
4500
4501   cav->wind_time = 9999;
4502   cav->wind_direction =
4503     map_direction_RND_to_EM(level->wind_direction_initial);
4504
4505   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4506     for (j = 0; j < 8; j++)
4507       cav->ball_array[i][j] =
4508         map_element_RND_to_EM_cave(level->ball_content[i].
4509                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4510
4511   map_android_clone_elements_RND_to_EM(level);
4512
4513   // first fill the complete playfield with the empty space element
4514   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4515     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4516       cav->cave[x][y] = Cblank;
4517
4518   // then copy the real level contents from level file into the playfield
4519   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4520   {
4521     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4522
4523     if (level->field[x][y] == EL_AMOEBA_DEAD)
4524       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4525
4526     cav->cave[x][y] = new_element;
4527   }
4528
4529   for (i = 0; i < MAX_PLAYERS; i++)
4530   {
4531     cav->player_x[i] = -1;
4532     cav->player_y[i] = -1;
4533   }
4534
4535   // initialize player positions and delete players from the playfield
4536   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4537   {
4538     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4539     {
4540       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4541
4542       cav->player_x[player_nr] = x;
4543       cav->player_y[player_nr] = y;
4544
4545       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4546     }
4547   }
4548 }
4549
4550 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4551 {
4552   static int ball_xy[8][2] =
4553   {
4554     { 0, 0 },
4555     { 1, 0 },
4556     { 2, 0 },
4557     { 0, 1 },
4558     { 2, 1 },
4559     { 0, 2 },
4560     { 1, 2 },
4561     { 2, 2 },
4562   };
4563   struct LevelInfo_EM *level_em = level->native_em_level;
4564   struct CAVE *cav = level_em->cav;
4565   int i, j, x, y;
4566
4567   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4568   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4569
4570   level->time        = cav->time_seconds;
4571   level->gems_needed = cav->gems_needed;
4572
4573   sprintf(level->name, "Level %d", level->file_info.nr);
4574
4575   level->score[SC_EMERALD]      = cav->emerald_score;
4576   level->score[SC_DIAMOND]      = cav->diamond_score;
4577   level->score[SC_ROBOT]        = cav->alien_score;
4578   level->score[SC_SPACESHIP]    = cav->tank_score;
4579   level->score[SC_BUG]          = cav->bug_score;
4580   level->score[SC_YAMYAM]       = cav->eater_score;
4581   level->score[SC_NUT]          = cav->nut_score;
4582   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4583   level->score[SC_KEY]          = cav->key_score;
4584   level->score[SC_TIME_BONUS]   = cav->exit_score;
4585
4586   level->num_yamyam_contents    = cav->num_eater_arrays;
4587
4588   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4589     for (y = 0; y < 3; y++)
4590       for (x = 0; x < 3; x++)
4591         level->yamyam_content[i].e[x][y] =
4592           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4593
4594   level->amoeba_speed           = cav->amoeba_time;
4595   level->time_magic_wall        = cav->wonderwall_time;
4596   level->time_wheel             = cav->wheel_time;
4597
4598   level->android_move_time      = cav->android_move_time;
4599   level->android_clone_time     = cav->android_clone_time;
4600   level->ball_random            = cav->ball_random;
4601   level->ball_active_initial    = cav->ball_active;
4602   level->ball_time              = cav->ball_time;
4603   level->num_ball_contents      = cav->num_ball_arrays;
4604
4605   level->lenses_score           = cav->lenses_score;
4606   level->magnify_score          = cav->magnify_score;
4607   level->slurp_score            = cav->slurp_score;
4608
4609   level->lenses_time            = cav->lenses_time;
4610   level->magnify_time           = cav->magnify_time;
4611
4612   level->wind_direction_initial =
4613     map_direction_EM_to_RND(cav->wind_direction);
4614
4615   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4616     for (j = 0; j < 8; j++)
4617       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4618         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4619
4620   map_android_clone_elements_EM_to_RND(level);
4621
4622   // convert the playfield (some elements need special treatment)
4623   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4624   {
4625     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4626
4627     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4628       new_element = EL_AMOEBA_DEAD;
4629
4630     level->field[x][y] = new_element;
4631   }
4632
4633   for (i = 0; i < MAX_PLAYERS; i++)
4634   {
4635     // in case of all players set to the same field, use the first player
4636     int nr = MAX_PLAYERS - i - 1;
4637     int jx = cav->player_x[nr];
4638     int jy = cav->player_y[nr];
4639
4640     if (jx != -1 && jy != -1)
4641       level->field[jx][jy] = EL_PLAYER_1 + nr;
4642   }
4643
4644   // time score is counted for each 10 seconds left in Emerald Mine levels
4645   level->time_score_base = 10;
4646 }
4647
4648
4649 // ----------------------------------------------------------------------------
4650 // functions for loading SP level
4651 // ----------------------------------------------------------------------------
4652
4653 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4654 {
4655   struct LevelInfo_SP *level_sp = level->native_sp_level;
4656   LevelInfoType *header = &level_sp->header;
4657   int i, x, y;
4658
4659   level_sp->width  = level->fieldx;
4660   level_sp->height = level->fieldy;
4661
4662   for (x = 0; x < level->fieldx; x++)
4663     for (y = 0; y < level->fieldy; y++)
4664       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4665
4666   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4667
4668   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4669     header->LevelTitle[i] = level->name[i];
4670   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4671
4672   header->InfotronsNeeded = level->gems_needed;
4673
4674   header->SpecialPortCount = 0;
4675
4676   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4677   {
4678     boolean gravity_port_found = FALSE;
4679     boolean gravity_port_valid = FALSE;
4680     int gravity_port_flag;
4681     int gravity_port_base_element;
4682     int element = level->field[x][y];
4683
4684     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4685         element <= EL_SP_GRAVITY_ON_PORT_UP)
4686     {
4687       gravity_port_found = TRUE;
4688       gravity_port_valid = TRUE;
4689       gravity_port_flag = 1;
4690       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4691     }
4692     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4693              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4694     {
4695       gravity_port_found = TRUE;
4696       gravity_port_valid = TRUE;
4697       gravity_port_flag = 0;
4698       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4699     }
4700     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4701              element <= EL_SP_GRAVITY_PORT_UP)
4702     {
4703       // change R'n'D style gravity inverting special port to normal port
4704       // (there are no gravity inverting ports in native Supaplex engine)
4705
4706       gravity_port_found = TRUE;
4707       gravity_port_valid = FALSE;
4708       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4709     }
4710
4711     if (gravity_port_found)
4712     {
4713       if (gravity_port_valid &&
4714           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4715       {
4716         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4717
4718         port->PortLocation = (y * level->fieldx + x) * 2;
4719         port->Gravity = gravity_port_flag;
4720
4721         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4722
4723         header->SpecialPortCount++;
4724       }
4725       else
4726       {
4727         // change special gravity port to normal port
4728
4729         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4730       }
4731
4732       level_sp->playfield[x][y] = element - EL_SP_START;
4733     }
4734   }
4735 }
4736
4737 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4738 {
4739   struct LevelInfo_SP *level_sp = level->native_sp_level;
4740   LevelInfoType *header = &level_sp->header;
4741   boolean num_invalid_elements = 0;
4742   int i, j, x, y;
4743
4744   level->fieldx = level_sp->width;
4745   level->fieldy = level_sp->height;
4746
4747   for (x = 0; x < level->fieldx; x++)
4748   {
4749     for (y = 0; y < level->fieldy; y++)
4750     {
4751       int element_old = level_sp->playfield[x][y];
4752       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4753
4754       if (element_new == EL_UNKNOWN)
4755       {
4756         num_invalid_elements++;
4757
4758         Debug("level:native:SP", "invalid element %d at position %d, %d",
4759               element_old, x, y);
4760       }
4761
4762       level->field[x][y] = element_new;
4763     }
4764   }
4765
4766   if (num_invalid_elements > 0)
4767     Warn("found %d invalid elements%s", num_invalid_elements,
4768          (!options.debug ? " (use '--debug' for more details)" : ""));
4769
4770   for (i = 0; i < MAX_PLAYERS; i++)
4771     level->initial_player_gravity[i] =
4772       (header->InitialGravity == 1 ? TRUE : FALSE);
4773
4774   // skip leading spaces
4775   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4776     if (header->LevelTitle[i] != ' ')
4777       break;
4778
4779   // copy level title
4780   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4781     level->name[j] = header->LevelTitle[i];
4782   level->name[j] = '\0';
4783
4784   // cut trailing spaces
4785   for (; j > 0; j--)
4786     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4787       level->name[j - 1] = '\0';
4788
4789   level->gems_needed = header->InfotronsNeeded;
4790
4791   for (i = 0; i < header->SpecialPortCount; i++)
4792   {
4793     SpecialPortType *port = &header->SpecialPort[i];
4794     int port_location = port->PortLocation;
4795     int gravity = port->Gravity;
4796     int port_x, port_y, port_element;
4797
4798     port_x = (port_location / 2) % level->fieldx;
4799     port_y = (port_location / 2) / level->fieldx;
4800
4801     if (port_x < 0 || port_x >= level->fieldx ||
4802         port_y < 0 || port_y >= level->fieldy)
4803     {
4804       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4805
4806       continue;
4807     }
4808
4809     port_element = level->field[port_x][port_y];
4810
4811     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4812         port_element > EL_SP_GRAVITY_PORT_UP)
4813     {
4814       Warn("no special port at position (%d, %d)", port_x, port_y);
4815
4816       continue;
4817     }
4818
4819     // change previous (wrong) gravity inverting special port to either
4820     // gravity enabling special port or gravity disabling special port
4821     level->field[port_x][port_y] +=
4822       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4823        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4824   }
4825
4826   // change special gravity ports without database entries to normal ports
4827   for (x = 0; x < level->fieldx; x++)
4828     for (y = 0; y < level->fieldy; y++)
4829       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4830           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4831         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4832
4833   level->time = 0;                      // no time limit
4834   level->amoeba_speed = 0;
4835   level->time_magic_wall = 0;
4836   level->time_wheel = 0;
4837   level->amoeba_content = EL_EMPTY;
4838
4839   // original Supaplex does not use score values -- rate by playing time
4840   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4841     level->score[i] = 0;
4842
4843   level->rate_time_over_score = TRUE;
4844
4845   // there are no yamyams in supaplex levels
4846   for (i = 0; i < level->num_yamyam_contents; i++)
4847     for (x = 0; x < 3; x++)
4848       for (y = 0; y < 3; y++)
4849         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4850 }
4851
4852 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4853 {
4854   struct LevelInfo_SP *level_sp = level->native_sp_level;
4855   struct DemoInfo_SP *demo = &level_sp->demo;
4856   int i, j;
4857
4858   // always start with reliable default values
4859   demo->is_available = FALSE;
4860   demo->length = 0;
4861
4862   if (TAPE_IS_EMPTY(tape))
4863     return;
4864
4865   demo->level_nr = tape.level_nr;       // (currently not used)
4866
4867   level_sp->header.DemoRandomSeed = tape.random_seed;
4868
4869   demo->length = 0;
4870
4871   for (i = 0; i < tape.length; i++)
4872   {
4873     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4874     int demo_repeat = tape.pos[i].delay;
4875     int demo_entries = (demo_repeat + 15) / 16;
4876
4877     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4878     {
4879       Warn("tape truncated: size exceeds maximum SP demo size %d",
4880            SP_MAX_TAPE_LEN);
4881
4882       break;
4883     }
4884
4885     for (j = 0; j < demo_repeat / 16; j++)
4886       demo->data[demo->length++] = 0xf0 | demo_action;
4887
4888     if (demo_repeat % 16)
4889       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4890   }
4891
4892   demo->is_available = TRUE;
4893 }
4894
4895 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4896 {
4897   struct LevelInfo_SP *level_sp = level->native_sp_level;
4898   struct DemoInfo_SP *demo = &level_sp->demo;
4899   char *filename = level->file_info.filename;
4900   int i;
4901
4902   // always start with reliable default values
4903   setTapeInfoToDefaults();
4904
4905   if (!demo->is_available)
4906     return;
4907
4908   tape.level_nr = demo->level_nr;       // (currently not used)
4909   tape.random_seed = level_sp->header.DemoRandomSeed;
4910
4911   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4912
4913   tape.counter = 0;
4914   tape.pos[tape.counter].delay = 0;
4915
4916   for (i = 0; i < demo->length; i++)
4917   {
4918     int demo_action = demo->data[i] & 0x0f;
4919     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4920     int tape_action = map_key_SP_to_RND(demo_action);
4921     int tape_repeat = demo_repeat + 1;
4922     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4923     boolean success = 0;
4924     int j;
4925
4926     for (j = 0; j < tape_repeat; j++)
4927       success = TapeAddAction(action);
4928
4929     if (!success)
4930     {
4931       Warn("SP demo truncated: size exceeds maximum tape size %d",
4932            MAX_TAPE_LEN);
4933
4934       break;
4935     }
4936   }
4937
4938   TapeHaltRecording();
4939 }
4940
4941
4942 // ----------------------------------------------------------------------------
4943 // functions for loading MM level
4944 // ----------------------------------------------------------------------------
4945
4946 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4947 {
4948   struct LevelInfo_MM *level_mm = level->native_mm_level;
4949   int i, x, y;
4950
4951   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4952   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4953
4954   level_mm->time = level->time;
4955   level_mm->kettles_needed = level->gems_needed;
4956   level_mm->auto_count_kettles = level->auto_count_gems;
4957
4958   level_mm->mm_laser_red   = level->mm_laser_red;
4959   level_mm->mm_laser_green = level->mm_laser_green;
4960   level_mm->mm_laser_blue  = level->mm_laser_blue;
4961
4962   level_mm->df_laser_red   = level->df_laser_red;
4963   level_mm->df_laser_green = level->df_laser_green;
4964   level_mm->df_laser_blue  = level->df_laser_blue;
4965
4966   strcpy(level_mm->name, level->name);
4967   strcpy(level_mm->author, level->author);
4968
4969   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4970   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4971   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4972   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4973   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4974
4975   level_mm->amoeba_speed = level->amoeba_speed;
4976   level_mm->time_fuse    = level->mm_time_fuse;
4977   level_mm->time_bomb    = level->mm_time_bomb;
4978   level_mm->time_ball    = level->mm_time_ball;
4979   level_mm->time_block   = level->mm_time_block;
4980
4981   level_mm->num_ball_contents = level->num_mm_ball_contents;
4982   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4983   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4984   level_mm->explode_ball = level->explode_mm_ball;
4985
4986   for (i = 0; i < level->num_mm_ball_contents; i++)
4987     level_mm->ball_content[i] =
4988       map_element_RND_to_MM(level->mm_ball_content[i]);
4989
4990   for (x = 0; x < level->fieldx; x++)
4991     for (y = 0; y < level->fieldy; y++)
4992       Ur[x][y] =
4993         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4994 }
4995
4996 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4997 {
4998   struct LevelInfo_MM *level_mm = level->native_mm_level;
4999   int i, x, y;
5000
5001   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5002   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5003
5004   level->time = level_mm->time;
5005   level->gems_needed = level_mm->kettles_needed;
5006   level->auto_count_gems = level_mm->auto_count_kettles;
5007
5008   level->mm_laser_red   = level_mm->mm_laser_red;
5009   level->mm_laser_green = level_mm->mm_laser_green;
5010   level->mm_laser_blue  = level_mm->mm_laser_blue;
5011
5012   level->df_laser_red   = level_mm->df_laser_red;
5013   level->df_laser_green = level_mm->df_laser_green;
5014   level->df_laser_blue  = level_mm->df_laser_blue;
5015
5016   strcpy(level->name, level_mm->name);
5017
5018   // only overwrite author from 'levelinfo.conf' if author defined in level
5019   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5020     strcpy(level->author, level_mm->author);
5021
5022   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5023   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5024   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5025   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5026   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5027
5028   level->amoeba_speed  = level_mm->amoeba_speed;
5029   level->mm_time_fuse  = level_mm->time_fuse;
5030   level->mm_time_bomb  = level_mm->time_bomb;
5031   level->mm_time_ball  = level_mm->time_ball;
5032   level->mm_time_block = level_mm->time_block;
5033
5034   level->num_mm_ball_contents = level_mm->num_ball_contents;
5035   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5036   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5037   level->explode_mm_ball = level_mm->explode_ball;
5038
5039   for (i = 0; i < level->num_mm_ball_contents; i++)
5040     level->mm_ball_content[i] =
5041       map_element_MM_to_RND(level_mm->ball_content[i]);
5042
5043   for (x = 0; x < level->fieldx; x++)
5044     for (y = 0; y < level->fieldy; y++)
5045       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5046 }
5047
5048
5049 // ----------------------------------------------------------------------------
5050 // functions for loading DC level
5051 // ----------------------------------------------------------------------------
5052
5053 #define DC_LEVEL_HEADER_SIZE            344
5054
5055 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5056                                         boolean init)
5057 {
5058   static int last_data_encoded;
5059   static int offset1;
5060   static int offset2;
5061   int diff;
5062   int diff_hi, diff_lo;
5063   int data_hi, data_lo;
5064   unsigned short data_decoded;
5065
5066   if (init)
5067   {
5068     last_data_encoded = 0;
5069     offset1 = -1;
5070     offset2 = 0;
5071
5072     return 0;
5073   }
5074
5075   diff = data_encoded - last_data_encoded;
5076   diff_hi = diff & ~0xff;
5077   diff_lo = diff &  0xff;
5078
5079   offset2 += diff_lo;
5080
5081   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5082   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5083   data_hi = data_hi & 0xff00;
5084
5085   data_decoded = data_hi | data_lo;
5086
5087   last_data_encoded = data_encoded;
5088
5089   offset1 = (offset1 + 1) % 31;
5090   offset2 = offset2 & 0xff;
5091
5092   return data_decoded;
5093 }
5094
5095 static int getMappedElement_DC(int element)
5096 {
5097   switch (element)
5098   {
5099     case 0x0000:
5100       element = EL_ROCK;
5101       break;
5102
5103       // 0x0117 - 0x036e: (?)
5104       // EL_DIAMOND
5105
5106       // 0x042d - 0x0684: (?)
5107       // EL_EMERALD
5108
5109     case 0x06f1:
5110       element = EL_NUT;
5111       break;
5112
5113     case 0x074c:
5114       element = EL_BOMB;
5115       break;
5116
5117     case 0x07a4:
5118       element = EL_PEARL;
5119       break;
5120
5121     case 0x0823:
5122       element = EL_CRYSTAL;
5123       break;
5124
5125     case 0x0e77:        // quicksand (boulder)
5126       element = EL_QUICKSAND_FAST_FULL;
5127       break;
5128
5129     case 0x0e99:        // slow quicksand (boulder)
5130       element = EL_QUICKSAND_FULL;
5131       break;
5132
5133     case 0x0ed2:
5134       element = EL_EM_EXIT_OPEN;
5135       break;
5136
5137     case 0x0ee3:
5138       element = EL_EM_EXIT_CLOSED;
5139       break;
5140
5141     case 0x0eeb:
5142       element = EL_EM_STEEL_EXIT_OPEN;
5143       break;
5144
5145     case 0x0efc:
5146       element = EL_EM_STEEL_EXIT_CLOSED;
5147       break;
5148
5149     case 0x0f4f:        // dynamite (lit 1)
5150       element = EL_EM_DYNAMITE_ACTIVE;
5151       break;
5152
5153     case 0x0f57:        // dynamite (lit 2)
5154       element = EL_EM_DYNAMITE_ACTIVE;
5155       break;
5156
5157     case 0x0f5f:        // dynamite (lit 3)
5158       element = EL_EM_DYNAMITE_ACTIVE;
5159       break;
5160
5161     case 0x0f67:        // dynamite (lit 4)
5162       element = EL_EM_DYNAMITE_ACTIVE;
5163       break;
5164
5165     case 0x0f81:
5166     case 0x0f82:
5167     case 0x0f83:
5168     case 0x0f84:
5169       element = EL_AMOEBA_WET;
5170       break;
5171
5172     case 0x0f85:
5173       element = EL_AMOEBA_DROP;
5174       break;
5175
5176     case 0x0fb9:
5177       element = EL_DC_MAGIC_WALL;
5178       break;
5179
5180     case 0x0fd0:
5181       element = EL_SPACESHIP_UP;
5182       break;
5183
5184     case 0x0fd9:
5185       element = EL_SPACESHIP_DOWN;
5186       break;
5187
5188     case 0x0ff1:
5189       element = EL_SPACESHIP_LEFT;
5190       break;
5191
5192     case 0x0ff9:
5193       element = EL_SPACESHIP_RIGHT;
5194       break;
5195
5196     case 0x1057:
5197       element = EL_BUG_UP;
5198       break;
5199
5200     case 0x1060:
5201       element = EL_BUG_DOWN;
5202       break;
5203
5204     case 0x1078:
5205       element = EL_BUG_LEFT;
5206       break;
5207
5208     case 0x1080:
5209       element = EL_BUG_RIGHT;
5210       break;
5211
5212     case 0x10de:
5213       element = EL_MOLE_UP;
5214       break;
5215
5216     case 0x10e7:
5217       element = EL_MOLE_DOWN;
5218       break;
5219
5220     case 0x10ff:
5221       element = EL_MOLE_LEFT;
5222       break;
5223
5224     case 0x1107:
5225       element = EL_MOLE_RIGHT;
5226       break;
5227
5228     case 0x11c0:
5229       element = EL_ROBOT;
5230       break;
5231
5232     case 0x13f5:
5233       element = EL_YAMYAM_UP;
5234       break;
5235
5236     case 0x1425:
5237       element = EL_SWITCHGATE_OPEN;
5238       break;
5239
5240     case 0x1426:
5241       element = EL_SWITCHGATE_CLOSED;
5242       break;
5243
5244     case 0x1437:
5245       element = EL_DC_SWITCHGATE_SWITCH_UP;
5246       break;
5247
5248     case 0x143a:
5249       element = EL_TIMEGATE_CLOSED;
5250       break;
5251
5252     case 0x144c:        // conveyor belt switch (green)
5253       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5254       break;
5255
5256     case 0x144f:        // conveyor belt switch (red)
5257       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5258       break;
5259
5260     case 0x1452:        // conveyor belt switch (blue)
5261       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5262       break;
5263
5264     case 0x145b:
5265       element = EL_CONVEYOR_BELT_3_MIDDLE;
5266       break;
5267
5268     case 0x1463:
5269       element = EL_CONVEYOR_BELT_3_LEFT;
5270       break;
5271
5272     case 0x146b:
5273       element = EL_CONVEYOR_BELT_3_RIGHT;
5274       break;
5275
5276     case 0x1473:
5277       element = EL_CONVEYOR_BELT_1_MIDDLE;
5278       break;
5279
5280     case 0x147b:
5281       element = EL_CONVEYOR_BELT_1_LEFT;
5282       break;
5283
5284     case 0x1483:
5285       element = EL_CONVEYOR_BELT_1_RIGHT;
5286       break;
5287
5288     case 0x148b:
5289       element = EL_CONVEYOR_BELT_4_MIDDLE;
5290       break;
5291
5292     case 0x1493:
5293       element = EL_CONVEYOR_BELT_4_LEFT;
5294       break;
5295
5296     case 0x149b:
5297       element = EL_CONVEYOR_BELT_4_RIGHT;
5298       break;
5299
5300     case 0x14ac:
5301       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5302       break;
5303
5304     case 0x14bd:
5305       element = EL_EXPANDABLE_WALL_VERTICAL;
5306       break;
5307
5308     case 0x14c6:
5309       element = EL_EXPANDABLE_WALL_ANY;
5310       break;
5311
5312     case 0x14ce:        // growing steel wall (left/right)
5313       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5314       break;
5315
5316     case 0x14df:        // growing steel wall (up/down)
5317       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5318       break;
5319
5320     case 0x14e8:        // growing steel wall (up/down/left/right)
5321       element = EL_EXPANDABLE_STEELWALL_ANY;
5322       break;
5323
5324     case 0x14e9:
5325       element = EL_SHIELD_DEADLY;
5326       break;
5327
5328     case 0x1501:
5329       element = EL_EXTRA_TIME;
5330       break;
5331
5332     case 0x154f:
5333       element = EL_ACID;
5334       break;
5335
5336     case 0x1577:
5337       element = EL_EMPTY_SPACE;
5338       break;
5339
5340     case 0x1578:        // quicksand (empty)
5341       element = EL_QUICKSAND_FAST_EMPTY;
5342       break;
5343
5344     case 0x1579:        // slow quicksand (empty)
5345       element = EL_QUICKSAND_EMPTY;
5346       break;
5347
5348       // 0x157c - 0x158b:
5349       // EL_SAND
5350
5351       // 0x1590 - 0x159f:
5352       // EL_DC_LANDMINE
5353
5354     case 0x15a0:
5355       element = EL_EM_DYNAMITE;
5356       break;
5357
5358     case 0x15a1:        // key (red)
5359       element = EL_EM_KEY_1;
5360       break;
5361
5362     case 0x15a2:        // key (yellow)
5363       element = EL_EM_KEY_2;
5364       break;
5365
5366     case 0x15a3:        // key (blue)
5367       element = EL_EM_KEY_4;
5368       break;
5369
5370     case 0x15a4:        // key (green)
5371       element = EL_EM_KEY_3;
5372       break;
5373
5374     case 0x15a5:        // key (white)
5375       element = EL_DC_KEY_WHITE;
5376       break;
5377
5378     case 0x15a6:
5379       element = EL_WALL_SLIPPERY;
5380       break;
5381
5382     case 0x15a7:
5383       element = EL_WALL;
5384       break;
5385
5386     case 0x15a8:        // wall (not round)
5387       element = EL_WALL;
5388       break;
5389
5390     case 0x15a9:        // (blue)
5391       element = EL_CHAR_A;
5392       break;
5393
5394     case 0x15aa:        // (blue)
5395       element = EL_CHAR_B;
5396       break;
5397
5398     case 0x15ab:        // (blue)
5399       element = EL_CHAR_C;
5400       break;
5401
5402     case 0x15ac:        // (blue)
5403       element = EL_CHAR_D;
5404       break;
5405
5406     case 0x15ad:        // (blue)
5407       element = EL_CHAR_E;
5408       break;
5409
5410     case 0x15ae:        // (blue)
5411       element = EL_CHAR_F;
5412       break;
5413
5414     case 0x15af:        // (blue)
5415       element = EL_CHAR_G;
5416       break;
5417
5418     case 0x15b0:        // (blue)
5419       element = EL_CHAR_H;
5420       break;
5421
5422     case 0x15b1:        // (blue)
5423       element = EL_CHAR_I;
5424       break;
5425
5426     case 0x15b2:        // (blue)
5427       element = EL_CHAR_J;
5428       break;
5429
5430     case 0x15b3:        // (blue)
5431       element = EL_CHAR_K;
5432       break;
5433
5434     case 0x15b4:        // (blue)
5435       element = EL_CHAR_L;
5436       break;
5437
5438     case 0x15b5:        // (blue)
5439       element = EL_CHAR_M;
5440       break;
5441
5442     case 0x15b6:        // (blue)
5443       element = EL_CHAR_N;
5444       break;
5445
5446     case 0x15b7:        // (blue)
5447       element = EL_CHAR_O;
5448       break;
5449
5450     case 0x15b8:        // (blue)
5451       element = EL_CHAR_P;
5452       break;
5453
5454     case 0x15b9:        // (blue)
5455       element = EL_CHAR_Q;
5456       break;
5457
5458     case 0x15ba:        // (blue)
5459       element = EL_CHAR_R;
5460       break;
5461
5462     case 0x15bb:        // (blue)
5463       element = EL_CHAR_S;
5464       break;
5465
5466     case 0x15bc:        // (blue)
5467       element = EL_CHAR_T;
5468       break;
5469
5470     case 0x15bd:        // (blue)
5471       element = EL_CHAR_U;
5472       break;
5473
5474     case 0x15be:        // (blue)
5475       element = EL_CHAR_V;
5476       break;
5477
5478     case 0x15bf:        // (blue)
5479       element = EL_CHAR_W;
5480       break;
5481
5482     case 0x15c0:        // (blue)
5483       element = EL_CHAR_X;
5484       break;
5485
5486     case 0x15c1:        // (blue)
5487       element = EL_CHAR_Y;
5488       break;
5489
5490     case 0x15c2:        // (blue)
5491       element = EL_CHAR_Z;
5492       break;
5493
5494     case 0x15c3:        // (blue)
5495       element = EL_CHAR_AUMLAUT;
5496       break;
5497
5498     case 0x15c4:        // (blue)
5499       element = EL_CHAR_OUMLAUT;
5500       break;
5501
5502     case 0x15c5:        // (blue)
5503       element = EL_CHAR_UUMLAUT;
5504       break;
5505
5506     case 0x15c6:        // (blue)
5507       element = EL_CHAR_0;
5508       break;
5509
5510     case 0x15c7:        // (blue)
5511       element = EL_CHAR_1;
5512       break;
5513
5514     case 0x15c8:        // (blue)
5515       element = EL_CHAR_2;
5516       break;
5517
5518     case 0x15c9:        // (blue)
5519       element = EL_CHAR_3;
5520       break;
5521
5522     case 0x15ca:        // (blue)
5523       element = EL_CHAR_4;
5524       break;
5525
5526     case 0x15cb:        // (blue)
5527       element = EL_CHAR_5;
5528       break;
5529
5530     case 0x15cc:        // (blue)
5531       element = EL_CHAR_6;
5532       break;
5533
5534     case 0x15cd:        // (blue)
5535       element = EL_CHAR_7;
5536       break;
5537
5538     case 0x15ce:        // (blue)
5539       element = EL_CHAR_8;
5540       break;
5541
5542     case 0x15cf:        // (blue)
5543       element = EL_CHAR_9;
5544       break;
5545
5546     case 0x15d0:        // (blue)
5547       element = EL_CHAR_PERIOD;
5548       break;
5549
5550     case 0x15d1:        // (blue)
5551       element = EL_CHAR_EXCLAM;
5552       break;
5553
5554     case 0x15d2:        // (blue)
5555       element = EL_CHAR_COLON;
5556       break;
5557
5558     case 0x15d3:        // (blue)
5559       element = EL_CHAR_LESS;
5560       break;
5561
5562     case 0x15d4:        // (blue)
5563       element = EL_CHAR_GREATER;
5564       break;
5565
5566     case 0x15d5:        // (blue)
5567       element = EL_CHAR_QUESTION;
5568       break;
5569
5570     case 0x15d6:        // (blue)
5571       element = EL_CHAR_COPYRIGHT;
5572       break;
5573
5574     case 0x15d7:        // (blue)
5575       element = EL_CHAR_UP;
5576       break;
5577
5578     case 0x15d8:        // (blue)
5579       element = EL_CHAR_DOWN;
5580       break;
5581
5582     case 0x15d9:        // (blue)
5583       element = EL_CHAR_BUTTON;
5584       break;
5585
5586     case 0x15da:        // (blue)
5587       element = EL_CHAR_PLUS;
5588       break;
5589
5590     case 0x15db:        // (blue)
5591       element = EL_CHAR_MINUS;
5592       break;
5593
5594     case 0x15dc:        // (blue)
5595       element = EL_CHAR_APOSTROPHE;
5596       break;
5597
5598     case 0x15dd:        // (blue)
5599       element = EL_CHAR_PARENLEFT;
5600       break;
5601
5602     case 0x15de:        // (blue)
5603       element = EL_CHAR_PARENRIGHT;
5604       break;
5605
5606     case 0x15df:        // (green)
5607       element = EL_CHAR_A;
5608       break;
5609
5610     case 0x15e0:        // (green)
5611       element = EL_CHAR_B;
5612       break;
5613
5614     case 0x15e1:        // (green)
5615       element = EL_CHAR_C;
5616       break;
5617
5618     case 0x15e2:        // (green)
5619       element = EL_CHAR_D;
5620       break;
5621
5622     case 0x15e3:        // (green)
5623       element = EL_CHAR_E;
5624       break;
5625
5626     case 0x15e4:        // (green)
5627       element = EL_CHAR_F;
5628       break;
5629
5630     case 0x15e5:        // (green)
5631       element = EL_CHAR_G;
5632       break;
5633
5634     case 0x15e6:        // (green)
5635       element = EL_CHAR_H;
5636       break;
5637
5638     case 0x15e7:        // (green)
5639       element = EL_CHAR_I;
5640       break;
5641
5642     case 0x15e8:        // (green)
5643       element = EL_CHAR_J;
5644       break;
5645
5646     case 0x15e9:        // (green)
5647       element = EL_CHAR_K;
5648       break;
5649
5650     case 0x15ea:        // (green)
5651       element = EL_CHAR_L;
5652       break;
5653
5654     case 0x15eb:        // (green)
5655       element = EL_CHAR_M;
5656       break;
5657
5658     case 0x15ec:        // (green)
5659       element = EL_CHAR_N;
5660       break;
5661
5662     case 0x15ed:        // (green)
5663       element = EL_CHAR_O;
5664       break;
5665
5666     case 0x15ee:        // (green)
5667       element = EL_CHAR_P;
5668       break;
5669
5670     case 0x15ef:        // (green)
5671       element = EL_CHAR_Q;
5672       break;
5673
5674     case 0x15f0:        // (green)
5675       element = EL_CHAR_R;
5676       break;
5677
5678     case 0x15f1:        // (green)
5679       element = EL_CHAR_S;
5680       break;
5681
5682     case 0x15f2:        // (green)
5683       element = EL_CHAR_T;
5684       break;
5685
5686     case 0x15f3:        // (green)
5687       element = EL_CHAR_U;
5688       break;
5689
5690     case 0x15f4:        // (green)
5691       element = EL_CHAR_V;
5692       break;
5693
5694     case 0x15f5:        // (green)
5695       element = EL_CHAR_W;
5696       break;
5697
5698     case 0x15f6:        // (green)
5699       element = EL_CHAR_X;
5700       break;
5701
5702     case 0x15f7:        // (green)
5703       element = EL_CHAR_Y;
5704       break;
5705
5706     case 0x15f8:        // (green)
5707       element = EL_CHAR_Z;
5708       break;
5709
5710     case 0x15f9:        // (green)
5711       element = EL_CHAR_AUMLAUT;
5712       break;
5713
5714     case 0x15fa:        // (green)
5715       element = EL_CHAR_OUMLAUT;
5716       break;
5717
5718     case 0x15fb:        // (green)
5719       element = EL_CHAR_UUMLAUT;
5720       break;
5721
5722     case 0x15fc:        // (green)
5723       element = EL_CHAR_0;
5724       break;
5725
5726     case 0x15fd:        // (green)
5727       element = EL_CHAR_1;
5728       break;
5729
5730     case 0x15fe:        // (green)
5731       element = EL_CHAR_2;
5732       break;
5733
5734     case 0x15ff:        // (green)
5735       element = EL_CHAR_3;
5736       break;
5737
5738     case 0x1600:        // (green)
5739       element = EL_CHAR_4;
5740       break;
5741
5742     case 0x1601:        // (green)
5743       element = EL_CHAR_5;
5744       break;
5745
5746     case 0x1602:        // (green)
5747       element = EL_CHAR_6;
5748       break;
5749
5750     case 0x1603:        // (green)
5751       element = EL_CHAR_7;
5752       break;
5753
5754     case 0x1604:        // (green)
5755       element = EL_CHAR_8;
5756       break;
5757
5758     case 0x1605:        // (green)
5759       element = EL_CHAR_9;
5760       break;
5761
5762     case 0x1606:        // (green)
5763       element = EL_CHAR_PERIOD;
5764       break;
5765
5766     case 0x1607:        // (green)
5767       element = EL_CHAR_EXCLAM;
5768       break;
5769
5770     case 0x1608:        // (green)
5771       element = EL_CHAR_COLON;
5772       break;
5773
5774     case 0x1609:        // (green)
5775       element = EL_CHAR_LESS;
5776       break;
5777
5778     case 0x160a:        // (green)
5779       element = EL_CHAR_GREATER;
5780       break;
5781
5782     case 0x160b:        // (green)
5783       element = EL_CHAR_QUESTION;
5784       break;
5785
5786     case 0x160c:        // (green)
5787       element = EL_CHAR_COPYRIGHT;
5788       break;
5789
5790     case 0x160d:        // (green)
5791       element = EL_CHAR_UP;
5792       break;
5793
5794     case 0x160e:        // (green)
5795       element = EL_CHAR_DOWN;
5796       break;
5797
5798     case 0x160f:        // (green)
5799       element = EL_CHAR_BUTTON;
5800       break;
5801
5802     case 0x1610:        // (green)
5803       element = EL_CHAR_PLUS;
5804       break;
5805
5806     case 0x1611:        // (green)
5807       element = EL_CHAR_MINUS;
5808       break;
5809
5810     case 0x1612:        // (green)
5811       element = EL_CHAR_APOSTROPHE;
5812       break;
5813
5814     case 0x1613:        // (green)
5815       element = EL_CHAR_PARENLEFT;
5816       break;
5817
5818     case 0x1614:        // (green)
5819       element = EL_CHAR_PARENRIGHT;
5820       break;
5821
5822     case 0x1615:        // (blue steel)
5823       element = EL_STEEL_CHAR_A;
5824       break;
5825
5826     case 0x1616:        // (blue steel)
5827       element = EL_STEEL_CHAR_B;
5828       break;
5829
5830     case 0x1617:        // (blue steel)
5831       element = EL_STEEL_CHAR_C;
5832       break;
5833
5834     case 0x1618:        // (blue steel)
5835       element = EL_STEEL_CHAR_D;
5836       break;
5837
5838     case 0x1619:        // (blue steel)
5839       element = EL_STEEL_CHAR_E;
5840       break;
5841
5842     case 0x161a:        // (blue steel)
5843       element = EL_STEEL_CHAR_F;
5844       break;
5845
5846     case 0x161b:        // (blue steel)
5847       element = EL_STEEL_CHAR_G;
5848       break;
5849
5850     case 0x161c:        // (blue steel)
5851       element = EL_STEEL_CHAR_H;
5852       break;
5853
5854     case 0x161d:        // (blue steel)
5855       element = EL_STEEL_CHAR_I;
5856       break;
5857
5858     case 0x161e:        // (blue steel)
5859       element = EL_STEEL_CHAR_J;
5860       break;
5861
5862     case 0x161f:        // (blue steel)
5863       element = EL_STEEL_CHAR_K;
5864       break;
5865
5866     case 0x1620:        // (blue steel)
5867       element = EL_STEEL_CHAR_L;
5868       break;
5869
5870     case 0x1621:        // (blue steel)
5871       element = EL_STEEL_CHAR_M;
5872       break;
5873
5874     case 0x1622:        // (blue steel)
5875       element = EL_STEEL_CHAR_N;
5876       break;
5877
5878     case 0x1623:        // (blue steel)
5879       element = EL_STEEL_CHAR_O;
5880       break;
5881
5882     case 0x1624:        // (blue steel)
5883       element = EL_STEEL_CHAR_P;
5884       break;
5885
5886     case 0x1625:        // (blue steel)
5887       element = EL_STEEL_CHAR_Q;
5888       break;
5889
5890     case 0x1626:        // (blue steel)
5891       element = EL_STEEL_CHAR_R;
5892       break;
5893
5894     case 0x1627:        // (blue steel)
5895       element = EL_STEEL_CHAR_S;
5896       break;
5897
5898     case 0x1628:        // (blue steel)
5899       element = EL_STEEL_CHAR_T;
5900       break;
5901
5902     case 0x1629:        // (blue steel)
5903       element = EL_STEEL_CHAR_U;
5904       break;
5905
5906     case 0x162a:        // (blue steel)
5907       element = EL_STEEL_CHAR_V;
5908       break;
5909
5910     case 0x162b:        // (blue steel)
5911       element = EL_STEEL_CHAR_W;
5912       break;
5913
5914     case 0x162c:        // (blue steel)
5915       element = EL_STEEL_CHAR_X;
5916       break;
5917
5918     case 0x162d:        // (blue steel)
5919       element = EL_STEEL_CHAR_Y;
5920       break;
5921
5922     case 0x162e:        // (blue steel)
5923       element = EL_STEEL_CHAR_Z;
5924       break;
5925
5926     case 0x162f:        // (blue steel)
5927       element = EL_STEEL_CHAR_AUMLAUT;
5928       break;
5929
5930     case 0x1630:        // (blue steel)
5931       element = EL_STEEL_CHAR_OUMLAUT;
5932       break;
5933
5934     case 0x1631:        // (blue steel)
5935       element = EL_STEEL_CHAR_UUMLAUT;
5936       break;
5937
5938     case 0x1632:        // (blue steel)
5939       element = EL_STEEL_CHAR_0;
5940       break;
5941
5942     case 0x1633:        // (blue steel)
5943       element = EL_STEEL_CHAR_1;
5944       break;
5945
5946     case 0x1634:        // (blue steel)
5947       element = EL_STEEL_CHAR_2;
5948       break;
5949
5950     case 0x1635:        // (blue steel)
5951       element = EL_STEEL_CHAR_3;
5952       break;
5953
5954     case 0x1636:        // (blue steel)
5955       element = EL_STEEL_CHAR_4;
5956       break;
5957
5958     case 0x1637:        // (blue steel)
5959       element = EL_STEEL_CHAR_5;
5960       break;
5961
5962     case 0x1638:        // (blue steel)
5963       element = EL_STEEL_CHAR_6;
5964       break;
5965
5966     case 0x1639:        // (blue steel)
5967       element = EL_STEEL_CHAR_7;
5968       break;
5969
5970     case 0x163a:        // (blue steel)
5971       element = EL_STEEL_CHAR_8;
5972       break;
5973
5974     case 0x163b:        // (blue steel)
5975       element = EL_STEEL_CHAR_9;
5976       break;
5977
5978     case 0x163c:        // (blue steel)
5979       element = EL_STEEL_CHAR_PERIOD;
5980       break;
5981
5982     case 0x163d:        // (blue steel)
5983       element = EL_STEEL_CHAR_EXCLAM;
5984       break;
5985
5986     case 0x163e:        // (blue steel)
5987       element = EL_STEEL_CHAR_COLON;
5988       break;
5989
5990     case 0x163f:        // (blue steel)
5991       element = EL_STEEL_CHAR_LESS;
5992       break;
5993
5994     case 0x1640:        // (blue steel)
5995       element = EL_STEEL_CHAR_GREATER;
5996       break;
5997
5998     case 0x1641:        // (blue steel)
5999       element = EL_STEEL_CHAR_QUESTION;
6000       break;
6001
6002     case 0x1642:        // (blue steel)
6003       element = EL_STEEL_CHAR_COPYRIGHT;
6004       break;
6005
6006     case 0x1643:        // (blue steel)
6007       element = EL_STEEL_CHAR_UP;
6008       break;
6009
6010     case 0x1644:        // (blue steel)
6011       element = EL_STEEL_CHAR_DOWN;
6012       break;
6013
6014     case 0x1645:        // (blue steel)
6015       element = EL_STEEL_CHAR_BUTTON;
6016       break;
6017
6018     case 0x1646:        // (blue steel)
6019       element = EL_STEEL_CHAR_PLUS;
6020       break;
6021
6022     case 0x1647:        // (blue steel)
6023       element = EL_STEEL_CHAR_MINUS;
6024       break;
6025
6026     case 0x1648:        // (blue steel)
6027       element = EL_STEEL_CHAR_APOSTROPHE;
6028       break;
6029
6030     case 0x1649:        // (blue steel)
6031       element = EL_STEEL_CHAR_PARENLEFT;
6032       break;
6033
6034     case 0x164a:        // (blue steel)
6035       element = EL_STEEL_CHAR_PARENRIGHT;
6036       break;
6037
6038     case 0x164b:        // (green steel)
6039       element = EL_STEEL_CHAR_A;
6040       break;
6041
6042     case 0x164c:        // (green steel)
6043       element = EL_STEEL_CHAR_B;
6044       break;
6045
6046     case 0x164d:        // (green steel)
6047       element = EL_STEEL_CHAR_C;
6048       break;
6049
6050     case 0x164e:        // (green steel)
6051       element = EL_STEEL_CHAR_D;
6052       break;
6053
6054     case 0x164f:        // (green steel)
6055       element = EL_STEEL_CHAR_E;
6056       break;
6057
6058     case 0x1650:        // (green steel)
6059       element = EL_STEEL_CHAR_F;
6060       break;
6061
6062     case 0x1651:        // (green steel)
6063       element = EL_STEEL_CHAR_G;
6064       break;
6065
6066     case 0x1652:        // (green steel)
6067       element = EL_STEEL_CHAR_H;
6068       break;
6069
6070     case 0x1653:        // (green steel)
6071       element = EL_STEEL_CHAR_I;
6072       break;
6073
6074     case 0x1654:        // (green steel)
6075       element = EL_STEEL_CHAR_J;
6076       break;
6077
6078     case 0x1655:        // (green steel)
6079       element = EL_STEEL_CHAR_K;
6080       break;
6081
6082     case 0x1656:        // (green steel)
6083       element = EL_STEEL_CHAR_L;
6084       break;
6085
6086     case 0x1657:        // (green steel)
6087       element = EL_STEEL_CHAR_M;
6088       break;
6089
6090     case 0x1658:        // (green steel)
6091       element = EL_STEEL_CHAR_N;
6092       break;
6093
6094     case 0x1659:        // (green steel)
6095       element = EL_STEEL_CHAR_O;
6096       break;
6097
6098     case 0x165a:        // (green steel)
6099       element = EL_STEEL_CHAR_P;
6100       break;
6101
6102     case 0x165b:        // (green steel)
6103       element = EL_STEEL_CHAR_Q;
6104       break;
6105
6106     case 0x165c:        // (green steel)
6107       element = EL_STEEL_CHAR_R;
6108       break;
6109
6110     case 0x165d:        // (green steel)
6111       element = EL_STEEL_CHAR_S;
6112       break;
6113
6114     case 0x165e:        // (green steel)
6115       element = EL_STEEL_CHAR_T;
6116       break;
6117
6118     case 0x165f:        // (green steel)
6119       element = EL_STEEL_CHAR_U;
6120       break;
6121
6122     case 0x1660:        // (green steel)
6123       element = EL_STEEL_CHAR_V;
6124       break;
6125
6126     case 0x1661:        // (green steel)
6127       element = EL_STEEL_CHAR_W;
6128       break;
6129
6130     case 0x1662:        // (green steel)
6131       element = EL_STEEL_CHAR_X;
6132       break;
6133
6134     case 0x1663:        // (green steel)
6135       element = EL_STEEL_CHAR_Y;
6136       break;
6137
6138     case 0x1664:        // (green steel)
6139       element = EL_STEEL_CHAR_Z;
6140       break;
6141
6142     case 0x1665:        // (green steel)
6143       element = EL_STEEL_CHAR_AUMLAUT;
6144       break;
6145
6146     case 0x1666:        // (green steel)
6147       element = EL_STEEL_CHAR_OUMLAUT;
6148       break;
6149
6150     case 0x1667:        // (green steel)
6151       element = EL_STEEL_CHAR_UUMLAUT;
6152       break;
6153
6154     case 0x1668:        // (green steel)
6155       element = EL_STEEL_CHAR_0;
6156       break;
6157
6158     case 0x1669:        // (green steel)
6159       element = EL_STEEL_CHAR_1;
6160       break;
6161
6162     case 0x166a:        // (green steel)
6163       element = EL_STEEL_CHAR_2;
6164       break;
6165
6166     case 0x166b:        // (green steel)
6167       element = EL_STEEL_CHAR_3;
6168       break;
6169
6170     case 0x166c:        // (green steel)
6171       element = EL_STEEL_CHAR_4;
6172       break;
6173
6174     case 0x166d:        // (green steel)
6175       element = EL_STEEL_CHAR_5;
6176       break;
6177
6178     case 0x166e:        // (green steel)
6179       element = EL_STEEL_CHAR_6;
6180       break;
6181
6182     case 0x166f:        // (green steel)
6183       element = EL_STEEL_CHAR_7;
6184       break;
6185
6186     case 0x1670:        // (green steel)
6187       element = EL_STEEL_CHAR_8;
6188       break;
6189
6190     case 0x1671:        // (green steel)
6191       element = EL_STEEL_CHAR_9;
6192       break;
6193
6194     case 0x1672:        // (green steel)
6195       element = EL_STEEL_CHAR_PERIOD;
6196       break;
6197
6198     case 0x1673:        // (green steel)
6199       element = EL_STEEL_CHAR_EXCLAM;
6200       break;
6201
6202     case 0x1674:        // (green steel)
6203       element = EL_STEEL_CHAR_COLON;
6204       break;
6205
6206     case 0x1675:        // (green steel)
6207       element = EL_STEEL_CHAR_LESS;
6208       break;
6209
6210     case 0x1676:        // (green steel)
6211       element = EL_STEEL_CHAR_GREATER;
6212       break;
6213
6214     case 0x1677:        // (green steel)
6215       element = EL_STEEL_CHAR_QUESTION;
6216       break;
6217
6218     case 0x1678:        // (green steel)
6219       element = EL_STEEL_CHAR_COPYRIGHT;
6220       break;
6221
6222     case 0x1679:        // (green steel)
6223       element = EL_STEEL_CHAR_UP;
6224       break;
6225
6226     case 0x167a:        // (green steel)
6227       element = EL_STEEL_CHAR_DOWN;
6228       break;
6229
6230     case 0x167b:        // (green steel)
6231       element = EL_STEEL_CHAR_BUTTON;
6232       break;
6233
6234     case 0x167c:        // (green steel)
6235       element = EL_STEEL_CHAR_PLUS;
6236       break;
6237
6238     case 0x167d:        // (green steel)
6239       element = EL_STEEL_CHAR_MINUS;
6240       break;
6241
6242     case 0x167e:        // (green steel)
6243       element = EL_STEEL_CHAR_APOSTROPHE;
6244       break;
6245
6246     case 0x167f:        // (green steel)
6247       element = EL_STEEL_CHAR_PARENLEFT;
6248       break;
6249
6250     case 0x1680:        // (green steel)
6251       element = EL_STEEL_CHAR_PARENRIGHT;
6252       break;
6253
6254     case 0x1681:        // gate (red)
6255       element = EL_EM_GATE_1;
6256       break;
6257
6258     case 0x1682:        // secret gate (red)
6259       element = EL_EM_GATE_1_GRAY;
6260       break;
6261
6262     case 0x1683:        // gate (yellow)
6263       element = EL_EM_GATE_2;
6264       break;
6265
6266     case 0x1684:        // secret gate (yellow)
6267       element = EL_EM_GATE_2_GRAY;
6268       break;
6269
6270     case 0x1685:        // gate (blue)
6271       element = EL_EM_GATE_4;
6272       break;
6273
6274     case 0x1686:        // secret gate (blue)
6275       element = EL_EM_GATE_4_GRAY;
6276       break;
6277
6278     case 0x1687:        // gate (green)
6279       element = EL_EM_GATE_3;
6280       break;
6281
6282     case 0x1688:        // secret gate (green)
6283       element = EL_EM_GATE_3_GRAY;
6284       break;
6285
6286     case 0x1689:        // gate (white)
6287       element = EL_DC_GATE_WHITE;
6288       break;
6289
6290     case 0x168a:        // secret gate (white)
6291       element = EL_DC_GATE_WHITE_GRAY;
6292       break;
6293
6294     case 0x168b:        // secret gate (no key)
6295       element = EL_DC_GATE_FAKE_GRAY;
6296       break;
6297
6298     case 0x168c:
6299       element = EL_ROBOT_WHEEL;
6300       break;
6301
6302     case 0x168d:
6303       element = EL_DC_TIMEGATE_SWITCH;
6304       break;
6305
6306     case 0x168e:
6307       element = EL_ACID_POOL_BOTTOM;
6308       break;
6309
6310     case 0x168f:
6311       element = EL_ACID_POOL_TOPLEFT;
6312       break;
6313
6314     case 0x1690:
6315       element = EL_ACID_POOL_TOPRIGHT;
6316       break;
6317
6318     case 0x1691:
6319       element = EL_ACID_POOL_BOTTOMLEFT;
6320       break;
6321
6322     case 0x1692:
6323       element = EL_ACID_POOL_BOTTOMRIGHT;
6324       break;
6325
6326     case 0x1693:
6327       element = EL_STEELWALL;
6328       break;
6329
6330     case 0x1694:
6331       element = EL_STEELWALL_SLIPPERY;
6332       break;
6333
6334     case 0x1695:        // steel wall (not round)
6335       element = EL_STEELWALL;
6336       break;
6337
6338     case 0x1696:        // steel wall (left)
6339       element = EL_DC_STEELWALL_1_LEFT;
6340       break;
6341
6342     case 0x1697:        // steel wall (bottom)
6343       element = EL_DC_STEELWALL_1_BOTTOM;
6344       break;
6345
6346     case 0x1698:        // steel wall (right)
6347       element = EL_DC_STEELWALL_1_RIGHT;
6348       break;
6349
6350     case 0x1699:        // steel wall (top)
6351       element = EL_DC_STEELWALL_1_TOP;
6352       break;
6353
6354     case 0x169a:        // steel wall (left/bottom)
6355       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6356       break;
6357
6358     case 0x169b:        // steel wall (right/bottom)
6359       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6360       break;
6361
6362     case 0x169c:        // steel wall (right/top)
6363       element = EL_DC_STEELWALL_1_TOPRIGHT;
6364       break;
6365
6366     case 0x169d:        // steel wall (left/top)
6367       element = EL_DC_STEELWALL_1_TOPLEFT;
6368       break;
6369
6370     case 0x169e:        // steel wall (right/bottom small)
6371       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6372       break;
6373
6374     case 0x169f:        // steel wall (left/bottom small)
6375       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6376       break;
6377
6378     case 0x16a0:        // steel wall (right/top small)
6379       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6380       break;
6381
6382     case 0x16a1:        // steel wall (left/top small)
6383       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6384       break;
6385
6386     case 0x16a2:        // steel wall (left/right)
6387       element = EL_DC_STEELWALL_1_VERTICAL;
6388       break;
6389
6390     case 0x16a3:        // steel wall (top/bottom)
6391       element = EL_DC_STEELWALL_1_HORIZONTAL;
6392       break;
6393
6394     case 0x16a4:        // steel wall 2 (left end)
6395       element = EL_DC_STEELWALL_2_LEFT;
6396       break;
6397
6398     case 0x16a5:        // steel wall 2 (right end)
6399       element = EL_DC_STEELWALL_2_RIGHT;
6400       break;
6401
6402     case 0x16a6:        // steel wall 2 (top end)
6403       element = EL_DC_STEELWALL_2_TOP;
6404       break;
6405
6406     case 0x16a7:        // steel wall 2 (bottom end)
6407       element = EL_DC_STEELWALL_2_BOTTOM;
6408       break;
6409
6410     case 0x16a8:        // steel wall 2 (left/right)
6411       element = EL_DC_STEELWALL_2_HORIZONTAL;
6412       break;
6413
6414     case 0x16a9:        // steel wall 2 (up/down)
6415       element = EL_DC_STEELWALL_2_VERTICAL;
6416       break;
6417
6418     case 0x16aa:        // steel wall 2 (mid)
6419       element = EL_DC_STEELWALL_2_MIDDLE;
6420       break;
6421
6422     case 0x16ab:
6423       element = EL_SIGN_EXCLAMATION;
6424       break;
6425
6426     case 0x16ac:
6427       element = EL_SIGN_RADIOACTIVITY;
6428       break;
6429
6430     case 0x16ad:
6431       element = EL_SIGN_STOP;
6432       break;
6433
6434     case 0x16ae:
6435       element = EL_SIGN_WHEELCHAIR;
6436       break;
6437
6438     case 0x16af:
6439       element = EL_SIGN_PARKING;
6440       break;
6441
6442     case 0x16b0:
6443       element = EL_SIGN_NO_ENTRY;
6444       break;
6445
6446     case 0x16b1:
6447       element = EL_SIGN_HEART;
6448       break;
6449
6450     case 0x16b2:
6451       element = EL_SIGN_GIVE_WAY;
6452       break;
6453
6454     case 0x16b3:
6455       element = EL_SIGN_ENTRY_FORBIDDEN;
6456       break;
6457
6458     case 0x16b4:
6459       element = EL_SIGN_EMERGENCY_EXIT;
6460       break;
6461
6462     case 0x16b5:
6463       element = EL_SIGN_YIN_YANG;
6464       break;
6465
6466     case 0x16b6:
6467       element = EL_WALL_EMERALD;
6468       break;
6469
6470     case 0x16b7:
6471       element = EL_WALL_DIAMOND;
6472       break;
6473
6474     case 0x16b8:
6475       element = EL_WALL_PEARL;
6476       break;
6477
6478     case 0x16b9:
6479       element = EL_WALL_CRYSTAL;
6480       break;
6481
6482     case 0x16ba:
6483       element = EL_INVISIBLE_WALL;
6484       break;
6485
6486     case 0x16bb:
6487       element = EL_INVISIBLE_STEELWALL;
6488       break;
6489
6490       // 0x16bc - 0x16cb:
6491       // EL_INVISIBLE_SAND
6492
6493     case 0x16cc:
6494       element = EL_LIGHT_SWITCH;
6495       break;
6496
6497     case 0x16cd:
6498       element = EL_ENVELOPE_1;
6499       break;
6500
6501     default:
6502       if (element >= 0x0117 && element <= 0x036e)       // (?)
6503         element = EL_DIAMOND;
6504       else if (element >= 0x042d && element <= 0x0684)  // (?)
6505         element = EL_EMERALD;
6506       else if (element >= 0x157c && element <= 0x158b)
6507         element = EL_SAND;
6508       else if (element >= 0x1590 && element <= 0x159f)
6509         element = EL_DC_LANDMINE;
6510       else if (element >= 0x16bc && element <= 0x16cb)
6511         element = EL_INVISIBLE_SAND;
6512       else
6513       {
6514         Warn("unknown Diamond Caves element 0x%04x", element);
6515
6516         element = EL_UNKNOWN;
6517       }
6518       break;
6519   }
6520
6521   return getMappedElement(element);
6522 }
6523
6524 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6525 {
6526   byte header[DC_LEVEL_HEADER_SIZE];
6527   int envelope_size;
6528   int envelope_header_pos = 62;
6529   int envelope_content_pos = 94;
6530   int level_name_pos = 251;
6531   int level_author_pos = 292;
6532   int envelope_header_len;
6533   int envelope_content_len;
6534   int level_name_len;
6535   int level_author_len;
6536   int fieldx, fieldy;
6537   int num_yamyam_contents;
6538   int i, x, y;
6539
6540   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6541
6542   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6543   {
6544     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6545
6546     header[i * 2 + 0] = header_word >> 8;
6547     header[i * 2 + 1] = header_word & 0xff;
6548   }
6549
6550   // read some values from level header to check level decoding integrity
6551   fieldx = header[6] | (header[7] << 8);
6552   fieldy = header[8] | (header[9] << 8);
6553   num_yamyam_contents = header[60] | (header[61] << 8);
6554
6555   // do some simple sanity checks to ensure that level was correctly decoded
6556   if (fieldx < 1 || fieldx > 256 ||
6557       fieldy < 1 || fieldy > 256 ||
6558       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6559   {
6560     level->no_valid_file = TRUE;
6561
6562     Warn("cannot decode level from stream -- using empty level");
6563
6564     return;
6565   }
6566
6567   // maximum envelope header size is 31 bytes
6568   envelope_header_len   = header[envelope_header_pos];
6569   // maximum envelope content size is 110 (156?) bytes
6570   envelope_content_len  = header[envelope_content_pos];
6571
6572   // maximum level title size is 40 bytes
6573   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6574   // maximum level author size is 30 (51?) bytes
6575   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6576
6577   envelope_size = 0;
6578
6579   for (i = 0; i < envelope_header_len; i++)
6580     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6581       level->envelope[0].text[envelope_size++] =
6582         header[envelope_header_pos + 1 + i];
6583
6584   if (envelope_header_len > 0 && envelope_content_len > 0)
6585   {
6586     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6587       level->envelope[0].text[envelope_size++] = '\n';
6588     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6589       level->envelope[0].text[envelope_size++] = '\n';
6590   }
6591
6592   for (i = 0; i < envelope_content_len; i++)
6593     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6594       level->envelope[0].text[envelope_size++] =
6595         header[envelope_content_pos + 1 + i];
6596
6597   level->envelope[0].text[envelope_size] = '\0';
6598
6599   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6600   level->envelope[0].ysize = 10;
6601   level->envelope[0].autowrap = TRUE;
6602   level->envelope[0].centered = TRUE;
6603
6604   for (i = 0; i < level_name_len; i++)
6605     level->name[i] = header[level_name_pos + 1 + i];
6606   level->name[level_name_len] = '\0';
6607
6608   for (i = 0; i < level_author_len; i++)
6609     level->author[i] = header[level_author_pos + 1 + i];
6610   level->author[level_author_len] = '\0';
6611
6612   num_yamyam_contents = header[60] | (header[61] << 8);
6613   level->num_yamyam_contents =
6614     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6615
6616   for (i = 0; i < num_yamyam_contents; i++)
6617   {
6618     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6619     {
6620       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6621       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6622
6623       if (i < MAX_ELEMENT_CONTENTS)
6624         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6625     }
6626   }
6627
6628   fieldx = header[6] | (header[7] << 8);
6629   fieldy = header[8] | (header[9] << 8);
6630   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6631   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6632
6633   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6634   {
6635     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6636     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6637
6638     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6639       level->field[x][y] = getMappedElement_DC(element_dc);
6640   }
6641
6642   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6643   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6644   level->field[x][y] = EL_PLAYER_1;
6645
6646   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6647   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6648   level->field[x][y] = EL_PLAYER_2;
6649
6650   level->gems_needed            = header[18] | (header[19] << 8);
6651
6652   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6653   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6654   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6655   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6656   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6657   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6658   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6659   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6660   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6661   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6662   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6663   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6664
6665   level->time                   = header[44] | (header[45] << 8);
6666
6667   level->amoeba_speed           = header[46] | (header[47] << 8);
6668   level->time_light             = header[48] | (header[49] << 8);
6669   level->time_timegate          = header[50] | (header[51] << 8);
6670   level->time_wheel             = header[52] | (header[53] << 8);
6671   level->time_magic_wall        = header[54] | (header[55] << 8);
6672   level->extra_time             = header[56] | (header[57] << 8);
6673   level->shield_normal_time     = header[58] | (header[59] << 8);
6674
6675   // shield and extra time elements do not have a score
6676   level->score[SC_SHIELD]       = 0;
6677   level->extra_time_score       = 0;
6678
6679   // set time for normal and deadly shields to the same value
6680   level->shield_deadly_time     = level->shield_normal_time;
6681
6682   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6683   // can slip down from flat walls, like normal walls and steel walls
6684   level->em_slippery_gems = TRUE;
6685
6686   // time score is counted for each 10 seconds left in Diamond Caves levels
6687   level->time_score_base = 10;
6688 }
6689
6690 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6691                                      struct LevelFileInfo *level_file_info,
6692                                      boolean level_info_only)
6693 {
6694   char *filename = level_file_info->filename;
6695   File *file;
6696   int num_magic_bytes = 8;
6697   char magic_bytes[num_magic_bytes + 1];
6698   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6699
6700   if (!(file = openFile(filename, MODE_READ)))
6701   {
6702     level->no_valid_file = TRUE;
6703
6704     if (!level_info_only)
6705       Warn("cannot read level '%s' -- using empty level", filename);
6706
6707     return;
6708   }
6709
6710   // fseek(file, 0x0000, SEEK_SET);
6711
6712   if (level_file_info->packed)
6713   {
6714     // read "magic bytes" from start of file
6715     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6716       magic_bytes[0] = '\0';
6717
6718     // check "magic bytes" for correct file format
6719     if (!strPrefix(magic_bytes, "DC2"))
6720     {
6721       level->no_valid_file = TRUE;
6722
6723       Warn("unknown DC level file '%s' -- using empty level", filename);
6724
6725       return;
6726     }
6727
6728     if (strPrefix(magic_bytes, "DC2Win95") ||
6729         strPrefix(magic_bytes, "DC2Win98"))
6730     {
6731       int position_first_level = 0x00fa;
6732       int extra_bytes = 4;
6733       int skip_bytes;
6734
6735       // advance file stream to first level inside the level package
6736       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6737
6738       // each block of level data is followed by block of non-level data
6739       num_levels_to_skip *= 2;
6740
6741       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6742       while (num_levels_to_skip >= 0)
6743       {
6744         // advance file stream to next level inside the level package
6745         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6746         {
6747           level->no_valid_file = TRUE;
6748
6749           Warn("cannot fseek in file '%s' -- using empty level", filename);
6750
6751           return;
6752         }
6753
6754         // skip apparently unused extra bytes following each level
6755         ReadUnusedBytesFromFile(file, extra_bytes);
6756
6757         // read size of next level in level package
6758         skip_bytes = getFile32BitLE(file);
6759
6760         num_levels_to_skip--;
6761       }
6762     }
6763     else
6764     {
6765       level->no_valid_file = TRUE;
6766
6767       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6768
6769       return;
6770     }
6771   }
6772
6773   LoadLevelFromFileStream_DC(file, level);
6774
6775   closeFile(file);
6776 }
6777
6778
6779 // ----------------------------------------------------------------------------
6780 // functions for loading SB level
6781 // ----------------------------------------------------------------------------
6782
6783 int getMappedElement_SB(int element_ascii, boolean use_ces)
6784 {
6785   static struct
6786   {
6787     int ascii;
6788     int sb;
6789     int ce;
6790   }
6791   sb_element_mapping[] =
6792   {
6793     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6794     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6795     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6796     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6797     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6798     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6799     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6800     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6801
6802     { 0,   -1,                      -1          },
6803   };
6804
6805   int i;
6806
6807   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6808     if (element_ascii == sb_element_mapping[i].ascii)
6809       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6810
6811   return EL_UNDEFINED;
6812 }
6813
6814 static void SetLevelSettings_SB(struct LevelInfo *level)
6815 {
6816   // time settings
6817   level->time = 0;
6818   level->use_step_counter = TRUE;
6819
6820   // score settings
6821   level->score[SC_TIME_BONUS] = 0;
6822   level->time_score_base = 1;
6823   level->rate_time_over_score = TRUE;
6824
6825   // game settings
6826   level->auto_exit_sokoban = TRUE;
6827 }
6828
6829 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6830                                      struct LevelFileInfo *level_file_info,
6831                                      boolean level_info_only)
6832 {
6833   char *filename = level_file_info->filename;
6834   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6835   char last_comment[MAX_LINE_LEN];
6836   char level_name[MAX_LINE_LEN];
6837   char *line_ptr;
6838   File *file;
6839   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6840   boolean read_continued_line = FALSE;
6841   boolean reading_playfield = FALSE;
6842   boolean got_valid_playfield_line = FALSE;
6843   boolean invalid_playfield_char = FALSE;
6844   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6845   int file_level_nr = 0;
6846   int x = 0, y = 0;             // initialized to make compilers happy
6847
6848   last_comment[0] = '\0';
6849   level_name[0] = '\0';
6850
6851   if (!(file = openFile(filename, MODE_READ)))
6852   {
6853     level->no_valid_file = TRUE;
6854
6855     if (!level_info_only)
6856       Warn("cannot read level '%s' -- using empty level", filename);
6857
6858     return;
6859   }
6860
6861   while (!checkEndOfFile(file))
6862   {
6863     // level successfully read, but next level may follow here
6864     if (!got_valid_playfield_line && reading_playfield)
6865     {
6866       // read playfield from single level file -- skip remaining file
6867       if (!level_file_info->packed)
6868         break;
6869
6870       if (file_level_nr >= num_levels_to_skip)
6871         break;
6872
6873       file_level_nr++;
6874
6875       last_comment[0] = '\0';
6876       level_name[0] = '\0';
6877
6878       reading_playfield = FALSE;
6879     }
6880
6881     got_valid_playfield_line = FALSE;
6882
6883     // read next line of input file
6884     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6885       break;
6886
6887     // cut trailing line break (this can be newline and/or carriage return)
6888     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6889       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6890         *line_ptr = '\0';
6891
6892     // copy raw input line for later use (mainly debugging output)
6893     strcpy(line_raw, line);
6894
6895     if (read_continued_line)
6896     {
6897       // append new line to existing line, if there is enough space
6898       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6899         strcat(previous_line, line_ptr);
6900
6901       strcpy(line, previous_line);      // copy storage buffer to line
6902
6903       read_continued_line = FALSE;
6904     }
6905
6906     // if the last character is '\', continue at next line
6907     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6908     {
6909       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6910       strcpy(previous_line, line);      // copy line to storage buffer
6911
6912       read_continued_line = TRUE;
6913
6914       continue;
6915     }
6916
6917     // skip empty lines
6918     if (line[0] == '\0')
6919       continue;
6920
6921     // extract comment text from comment line
6922     if (line[0] == ';')
6923     {
6924       for (line_ptr = line; *line_ptr; line_ptr++)
6925         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6926           break;
6927
6928       strcpy(last_comment, line_ptr);
6929
6930       continue;
6931     }
6932
6933     // extract level title text from line containing level title
6934     if (line[0] == '\'')
6935     {
6936       strcpy(level_name, &line[1]);
6937
6938       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6939         level_name[strlen(level_name) - 1] = '\0';
6940
6941       continue;
6942     }
6943
6944     // skip lines containing only spaces (or empty lines)
6945     for (line_ptr = line; *line_ptr; line_ptr++)
6946       if (*line_ptr != ' ')
6947         break;
6948     if (*line_ptr == '\0')
6949       continue;
6950
6951     // at this point, we have found a line containing part of a playfield
6952
6953     got_valid_playfield_line = TRUE;
6954
6955     if (!reading_playfield)
6956     {
6957       reading_playfield = TRUE;
6958       invalid_playfield_char = FALSE;
6959
6960       for (x = 0; x < MAX_LEV_FIELDX; x++)
6961         for (y = 0; y < MAX_LEV_FIELDY; y++)
6962           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6963
6964       level->fieldx = 0;
6965       level->fieldy = 0;
6966
6967       // start with topmost tile row
6968       y = 0;
6969     }
6970
6971     // skip playfield line if larger row than allowed
6972     if (y >= MAX_LEV_FIELDY)
6973       continue;
6974
6975     // start with leftmost tile column
6976     x = 0;
6977
6978     // read playfield elements from line
6979     for (line_ptr = line; *line_ptr; line_ptr++)
6980     {
6981       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6982
6983       // stop parsing playfield line if larger column than allowed
6984       if (x >= MAX_LEV_FIELDX)
6985         break;
6986
6987       if (mapped_sb_element == EL_UNDEFINED)
6988       {
6989         invalid_playfield_char = TRUE;
6990
6991         break;
6992       }
6993
6994       level->field[x][y] = mapped_sb_element;
6995
6996       // continue with next tile column
6997       x++;
6998
6999       level->fieldx = MAX(x, level->fieldx);
7000     }
7001
7002     if (invalid_playfield_char)
7003     {
7004       // if first playfield line, treat invalid lines as comment lines
7005       if (y == 0)
7006         reading_playfield = FALSE;
7007
7008       continue;
7009     }
7010
7011     // continue with next tile row
7012     y++;
7013   }
7014
7015   closeFile(file);
7016
7017   level->fieldy = y;
7018
7019   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7020   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7021
7022   if (!reading_playfield)
7023   {
7024     level->no_valid_file = TRUE;
7025
7026     Warn("cannot read level '%s' -- using empty level", filename);
7027
7028     return;
7029   }
7030
7031   if (*level_name != '\0')
7032   {
7033     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7034     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7035   }
7036   else if (*last_comment != '\0')
7037   {
7038     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7039     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7040   }
7041   else
7042   {
7043     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7044   }
7045
7046   // set all empty fields beyond the border walls to invisible steel wall
7047   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7048   {
7049     if ((x == 0 || x == level->fieldx - 1 ||
7050          y == 0 || y == level->fieldy - 1) &&
7051         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7052       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7053                      level->field, level->fieldx, level->fieldy);
7054   }
7055
7056   // set special level settings for Sokoban levels
7057   SetLevelSettings_SB(level);
7058
7059   if (load_xsb_to_ces)
7060   {
7061     // special global settings can now be set in level template
7062     level->use_custom_template = TRUE;
7063   }
7064 }
7065
7066
7067 // -------------------------------------------------------------------------
7068 // functions for handling native levels
7069 // -------------------------------------------------------------------------
7070
7071 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7072                                      struct LevelFileInfo *level_file_info,
7073                                      boolean level_info_only)
7074 {
7075   int pos = 0;
7076
7077   // determine position of requested level inside level package
7078   if (level_file_info->packed)
7079     pos = level_file_info->nr - leveldir_current->first_level;
7080
7081   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7082     level->no_valid_file = TRUE;
7083 }
7084
7085 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7086                                      struct LevelFileInfo *level_file_info,
7087                                      boolean level_info_only)
7088 {
7089   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7090     level->no_valid_file = TRUE;
7091 }
7092
7093 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7094                                      struct LevelFileInfo *level_file_info,
7095                                      boolean level_info_only)
7096 {
7097   int pos = 0;
7098
7099   // determine position of requested level inside level package
7100   if (level_file_info->packed)
7101     pos = level_file_info->nr - leveldir_current->first_level;
7102
7103   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7104     level->no_valid_file = TRUE;
7105 }
7106
7107 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7108                                      struct LevelFileInfo *level_file_info,
7109                                      boolean level_info_only)
7110 {
7111   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7112     level->no_valid_file = TRUE;
7113 }
7114
7115 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7116 {
7117   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7118     CopyNativeLevel_RND_to_BD(level);
7119   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7120     CopyNativeLevel_RND_to_EM(level);
7121   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7122     CopyNativeLevel_RND_to_SP(level);
7123   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7124     CopyNativeLevel_RND_to_MM(level);
7125 }
7126
7127 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7128 {
7129   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7130     CopyNativeLevel_BD_to_RND(level);
7131   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7132     CopyNativeLevel_EM_to_RND(level);
7133   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7134     CopyNativeLevel_SP_to_RND(level);
7135   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7136     CopyNativeLevel_MM_to_RND(level);
7137 }
7138
7139 void SaveNativeLevel(struct LevelInfo *level)
7140 {
7141   // saving native level files only supported for some game engines
7142   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7143       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7144     return;
7145
7146   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7147                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7148   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7149   char *filename = getLevelFilenameFromBasename(basename);
7150
7151   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7152     return;
7153
7154   boolean success = FALSE;
7155
7156   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7157   {
7158     CopyNativeLevel_RND_to_BD(level);
7159     // CopyNativeTape_RND_to_BD(level);
7160
7161     success = SaveNativeLevel_BD(filename);
7162   }
7163   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7164   {
7165     CopyNativeLevel_RND_to_SP(level);
7166     CopyNativeTape_RND_to_SP(level);
7167
7168     success = SaveNativeLevel_SP(filename);
7169   }
7170
7171   if (success)
7172     Request("Native level file saved!", REQ_CONFIRM);
7173   else
7174     Request("Failed to save native level file!", REQ_CONFIRM);
7175 }
7176
7177
7178 // ----------------------------------------------------------------------------
7179 // functions for loading generic level
7180 // ----------------------------------------------------------------------------
7181
7182 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7183                                   struct LevelFileInfo *level_file_info,
7184                                   boolean level_info_only)
7185 {
7186   // always start with reliable default values
7187   setLevelInfoToDefaults(level, level_info_only, TRUE);
7188
7189   switch (level_file_info->type)
7190   {
7191     case LEVEL_FILE_TYPE_RND:
7192       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7193       break;
7194
7195     case LEVEL_FILE_TYPE_BD:
7196       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7197       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7198       break;
7199
7200     case LEVEL_FILE_TYPE_EM:
7201       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7202       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7203       break;
7204
7205     case LEVEL_FILE_TYPE_SP:
7206       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7207       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7208       break;
7209
7210     case LEVEL_FILE_TYPE_MM:
7211       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7212       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7213       break;
7214
7215     case LEVEL_FILE_TYPE_DC:
7216       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7217       break;
7218
7219     case LEVEL_FILE_TYPE_SB:
7220       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7221       break;
7222
7223     default:
7224       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7225       break;
7226   }
7227
7228   // if level file is invalid, restore level structure to default values
7229   if (level->no_valid_file)
7230     setLevelInfoToDefaults(level, level_info_only, FALSE);
7231
7232   if (check_special_flags("use_native_bd_game_engine"))
7233     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7234
7235   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7236     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7237
7238   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7239     CopyNativeLevel_Native_to_RND(level);
7240 }
7241
7242 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7243 {
7244   static struct LevelFileInfo level_file_info;
7245
7246   // always start with reliable default values
7247   setFileInfoToDefaults(&level_file_info);
7248
7249   level_file_info.nr = 0;                       // unknown level number
7250   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7251
7252   setString(&level_file_info.filename, filename);
7253
7254   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7255 }
7256
7257 static void LoadLevel_InitVersion(struct LevelInfo *level)
7258 {
7259   int i, j;
7260
7261   if (leveldir_current == NULL)         // only when dumping level
7262     return;
7263
7264   // all engine modifications also valid for levels which use latest engine
7265   if (level->game_version < VERSION_IDENT(3,2,0,5))
7266   {
7267     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7268     level->time_score_base = 10;
7269   }
7270
7271   if (leveldir_current->latest_engine)
7272   {
7273     // ---------- use latest game engine --------------------------------------
7274
7275     /* For all levels which are forced to use the latest game engine version
7276        (normally all but user contributed, private and undefined levels), set
7277        the game engine version to the actual version; this allows for actual
7278        corrections in the game engine to take effect for existing, converted
7279        levels (from "classic" or other existing games) to make the emulation
7280        of the corresponding game more accurate, while (hopefully) not breaking
7281        existing levels created from other players. */
7282
7283     level->game_version = GAME_VERSION_ACTUAL;
7284
7285     /* Set special EM style gems behaviour: EM style gems slip down from
7286        normal, steel and growing wall. As this is a more fundamental change,
7287        it seems better to set the default behaviour to "off" (as it is more
7288        natural) and make it configurable in the level editor (as a property
7289        of gem style elements). Already existing converted levels (neither
7290        private nor contributed levels) are changed to the new behaviour. */
7291
7292     if (level->file_version < FILE_VERSION_2_0)
7293       level->em_slippery_gems = TRUE;
7294
7295     return;
7296   }
7297
7298   // ---------- use game engine the level was created with --------------------
7299
7300   /* For all levels which are not forced to use the latest game engine
7301      version (normally user contributed, private and undefined levels),
7302      use the version of the game engine the levels were created for.
7303
7304      Since 2.0.1, the game engine version is now directly stored
7305      in the level file (chunk "VERS"), so there is no need anymore
7306      to set the game version from the file version (except for old,
7307      pre-2.0 levels, where the game version is still taken from the
7308      file format version used to store the level -- see above). */
7309
7310   // player was faster than enemies in 1.0.0 and before
7311   if (level->file_version == FILE_VERSION_1_0)
7312     for (i = 0; i < MAX_PLAYERS; i++)
7313       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7314
7315   // default behaviour for EM style gems was "slippery" only in 2.0.1
7316   if (level->game_version == VERSION_IDENT(2,0,1,0))
7317     level->em_slippery_gems = TRUE;
7318
7319   // springs could be pushed over pits before (pre-release version) 2.2.0
7320   if (level->game_version < VERSION_IDENT(2,2,0,0))
7321     level->use_spring_bug = TRUE;
7322
7323   if (level->game_version < VERSION_IDENT(3,2,0,5))
7324   {
7325     // time orb caused limited time in endless time levels before 3.2.0-5
7326     level->use_time_orb_bug = TRUE;
7327
7328     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7329     level->block_snap_field = FALSE;
7330
7331     // extra time score was same value as time left score before 3.2.0-5
7332     level->extra_time_score = level->score[SC_TIME_BONUS];
7333   }
7334
7335   if (level->game_version < VERSION_IDENT(3,2,0,7))
7336   {
7337     // default behaviour for snapping was "not continuous" before 3.2.0-7
7338     level->continuous_snapping = FALSE;
7339   }
7340
7341   // only few elements were able to actively move into acid before 3.1.0
7342   // trigger settings did not exist before 3.1.0; set to default "any"
7343   if (level->game_version < VERSION_IDENT(3,1,0,0))
7344   {
7345     // correct "can move into acid" settings (all zero in old levels)
7346
7347     level->can_move_into_acid_bits = 0; // nothing can move into acid
7348     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7349
7350     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7351     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7352     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7353     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7354
7355     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7356       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7357
7358     // correct trigger settings (stored as zero == "none" in old levels)
7359
7360     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7361     {
7362       int element = EL_CUSTOM_START + i;
7363       struct ElementInfo *ei = &element_info[element];
7364
7365       for (j = 0; j < ei->num_change_pages; j++)
7366       {
7367         struct ElementChangeInfo *change = &ei->change_page[j];
7368
7369         change->trigger_player = CH_PLAYER_ANY;
7370         change->trigger_page = CH_PAGE_ANY;
7371       }
7372     }
7373   }
7374
7375   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7376   {
7377     int element = EL_CUSTOM_256;
7378     struct ElementInfo *ei = &element_info[element];
7379     struct ElementChangeInfo *change = &ei->change_page[0];
7380
7381     /* This is needed to fix a problem that was caused by a bugfix in function
7382        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7383        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7384        not replace walkable elements, but instead just placed the player on it,
7385        without placing the Sokoban field under the player). Unfortunately, this
7386        breaks "Snake Bite" style levels when the snake is halfway through a door
7387        that just closes (the snake head is still alive and can be moved in this
7388        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7389        player (without Sokoban element) which then gets killed as designed). */
7390
7391     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7392          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7393         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7394       change->target_element = EL_PLAYER_1;
7395   }
7396
7397   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7398   if (level->game_version < VERSION_IDENT(3,2,5,0))
7399   {
7400     /* This is needed to fix a problem that was caused by a bugfix in function
7401        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7402        corrects the behaviour when a custom element changes to another custom
7403        element with a higher element number that has change actions defined.
7404        Normally, only one change per frame is allowed for custom elements.
7405        Therefore, it is checked if a custom element already changed in the
7406        current frame; if it did, subsequent changes are suppressed.
7407        Unfortunately, this is only checked for element changes, but not for
7408        change actions, which are still executed. As the function above loops
7409        through all custom elements from lower to higher, an element change
7410        resulting in a lower CE number won't be checked again, while a target
7411        element with a higher number will also be checked, and potential change
7412        actions will get executed for this CE, too (which is wrong), while
7413        further changes are ignored (which is correct). As this bugfix breaks
7414        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7415        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7416        behaviour for existing levels and tapes that make use of this bug */
7417
7418     level->use_action_after_change_bug = TRUE;
7419   }
7420
7421   // not centering level after relocating player was default only in 3.2.3
7422   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7423     level->shifted_relocation = TRUE;
7424
7425   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7426   if (level->game_version < VERSION_IDENT(3,2,6,0))
7427     level->em_explodes_by_fire = TRUE;
7428
7429   // levels were solved by the first player entering an exit up to 4.1.0.0
7430   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7431     level->solved_by_one_player = TRUE;
7432
7433   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7434   if (level->game_version < VERSION_IDENT(4,1,1,1))
7435     level->use_life_bugs = TRUE;
7436
7437   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7438   if (level->game_version < VERSION_IDENT(4,1,1,1))
7439     level->sb_objects_needed = FALSE;
7440
7441   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7442   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7443     level->finish_dig_collect = FALSE;
7444
7445   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7446   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7447     level->keep_walkable_ce = TRUE;
7448 }
7449
7450 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7451 {
7452   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7453   int x, y;
7454
7455   // check if this level is (not) a Sokoban level
7456   for (y = 0; y < level->fieldy; y++)
7457     for (x = 0; x < level->fieldx; x++)
7458       if (!IS_SB_ELEMENT(Tile[x][y]))
7459         is_sokoban_level = FALSE;
7460
7461   if (is_sokoban_level)
7462   {
7463     // set special level settings for Sokoban levels
7464     SetLevelSettings_SB(level);
7465   }
7466 }
7467
7468 static void LoadLevel_InitSettings(struct LevelInfo *level)
7469 {
7470   // adjust level settings for (non-native) Sokoban-style levels
7471   LoadLevel_InitSettings_SB(level);
7472
7473   // rename levels with title "nameless level" or if renaming is forced
7474   if (leveldir_current->empty_level_name != NULL &&
7475       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7476        leveldir_current->force_level_name))
7477     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7478              leveldir_current->empty_level_name, level_nr);
7479 }
7480
7481 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7482 {
7483   int i, x, y;
7484
7485   // map elements that have changed in newer versions
7486   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7487                                                     level->game_version);
7488   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7489     for (x = 0; x < 3; x++)
7490       for (y = 0; y < 3; y++)
7491         level->yamyam_content[i].e[x][y] =
7492           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7493                                     level->game_version);
7494
7495 }
7496
7497 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7498 {
7499   int i, j;
7500
7501   // map custom element change events that have changed in newer versions
7502   // (these following values were accidentally changed in version 3.0.1)
7503   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7504   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7505   {
7506     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7507     {
7508       int element = EL_CUSTOM_START + i;
7509
7510       // order of checking and copying events to be mapped is important
7511       // (do not change the start and end value -- they are constant)
7512       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7513       {
7514         if (HAS_CHANGE_EVENT(element, j - 2))
7515         {
7516           SET_CHANGE_EVENT(element, j - 2, FALSE);
7517           SET_CHANGE_EVENT(element, j, TRUE);
7518         }
7519       }
7520
7521       // order of checking and copying events to be mapped is important
7522       // (do not change the start and end value -- they are constant)
7523       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7524       {
7525         if (HAS_CHANGE_EVENT(element, j - 1))
7526         {
7527           SET_CHANGE_EVENT(element, j - 1, FALSE);
7528           SET_CHANGE_EVENT(element, j, TRUE);
7529         }
7530       }
7531     }
7532   }
7533
7534   // initialize "can_change" field for old levels with only one change page
7535   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7536   {
7537     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7538     {
7539       int element = EL_CUSTOM_START + i;
7540
7541       if (CAN_CHANGE(element))
7542         element_info[element].change->can_change = TRUE;
7543     }
7544   }
7545
7546   // correct custom element values (for old levels without these options)
7547   if (level->game_version < VERSION_IDENT(3,1,1,0))
7548   {
7549     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7550     {
7551       int element = EL_CUSTOM_START + i;
7552       struct ElementInfo *ei = &element_info[element];
7553
7554       if (ei->access_direction == MV_NO_DIRECTION)
7555         ei->access_direction = MV_ALL_DIRECTIONS;
7556     }
7557   }
7558
7559   // correct custom element values (fix invalid values for all versions)
7560   if (1)
7561   {
7562     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7563     {
7564       int element = EL_CUSTOM_START + i;
7565       struct ElementInfo *ei = &element_info[element];
7566
7567       for (j = 0; j < ei->num_change_pages; j++)
7568       {
7569         struct ElementChangeInfo *change = &ei->change_page[j];
7570
7571         if (change->trigger_player == CH_PLAYER_NONE)
7572           change->trigger_player = CH_PLAYER_ANY;
7573
7574         if (change->trigger_side == CH_SIDE_NONE)
7575           change->trigger_side = CH_SIDE_ANY;
7576       }
7577     }
7578   }
7579
7580   // initialize "can_explode" field for old levels which did not store this
7581   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7582   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7583   {
7584     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7585     {
7586       int element = EL_CUSTOM_START + i;
7587
7588       if (EXPLODES_1X1_OLD(element))
7589         element_info[element].explosion_type = EXPLODES_1X1;
7590
7591       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7592                                              EXPLODES_SMASHED(element) ||
7593                                              EXPLODES_IMPACT(element)));
7594     }
7595   }
7596
7597   // correct previously hard-coded move delay values for maze runner style
7598   if (level->game_version < VERSION_IDENT(3,1,1,0))
7599   {
7600     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7601     {
7602       int element = EL_CUSTOM_START + i;
7603
7604       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7605       {
7606         // previously hard-coded and therefore ignored
7607         element_info[element].move_delay_fixed = 9;
7608         element_info[element].move_delay_random = 0;
7609       }
7610     }
7611   }
7612
7613   // set some other uninitialized values of custom elements in older levels
7614   if (level->game_version < VERSION_IDENT(3,1,0,0))
7615   {
7616     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7617     {
7618       int element = EL_CUSTOM_START + i;
7619
7620       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7621
7622       element_info[element].explosion_delay = 17;
7623       element_info[element].ignition_delay = 8;
7624     }
7625   }
7626
7627   // set mouse click change events to work for left/middle/right mouse button
7628   if (level->game_version < VERSION_IDENT(4,2,3,0))
7629   {
7630     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7631     {
7632       int element = EL_CUSTOM_START + i;
7633       struct ElementInfo *ei = &element_info[element];
7634
7635       for (j = 0; j < ei->num_change_pages; j++)
7636       {
7637         struct ElementChangeInfo *change = &ei->change_page[j];
7638
7639         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7640             change->has_event[CE_PRESSED_BY_MOUSE] ||
7641             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7642             change->has_event[CE_MOUSE_PRESSED_ON_X])
7643           change->trigger_side = CH_SIDE_ANY;
7644       }
7645     }
7646   }
7647 }
7648
7649 static void LoadLevel_InitElements(struct LevelInfo *level)
7650 {
7651   LoadLevel_InitStandardElements(level);
7652
7653   if (level->file_has_custom_elements)
7654     LoadLevel_InitCustomElements(level);
7655
7656   // initialize element properties for level editor etc.
7657   InitElementPropertiesEngine(level->game_version);
7658   InitElementPropertiesGfxElement();
7659 }
7660
7661 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7662 {
7663   int x, y;
7664
7665   // map elements that have changed in newer versions
7666   for (y = 0; y < level->fieldy; y++)
7667     for (x = 0; x < level->fieldx; x++)
7668       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7669                                                      level->game_version);
7670
7671   // clear unused playfield data (nicer if level gets resized in editor)
7672   for (x = 0; x < MAX_LEV_FIELDX; x++)
7673     for (y = 0; y < MAX_LEV_FIELDY; y++)
7674       if (x >= level->fieldx || y >= level->fieldy)
7675         level->field[x][y] = EL_EMPTY;
7676
7677   // copy elements to runtime playfield array
7678   for (x = 0; x < MAX_LEV_FIELDX; x++)
7679     for (y = 0; y < MAX_LEV_FIELDY; y++)
7680       Tile[x][y] = level->field[x][y];
7681
7682   // initialize level size variables for faster access
7683   lev_fieldx = level->fieldx;
7684   lev_fieldy = level->fieldy;
7685
7686   // determine border element for this level
7687   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7688     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7689   else
7690     SetBorderElement();
7691 }
7692
7693 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7694 {
7695   struct LevelFileInfo *level_file_info = &level->file_info;
7696
7697   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7698     CopyNativeLevel_RND_to_Native(level);
7699 }
7700
7701 static void LoadLevelTemplate_LoadAndInit(void)
7702 {
7703   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7704
7705   LoadLevel_InitVersion(&level_template);
7706   LoadLevel_InitElements(&level_template);
7707   LoadLevel_InitSettings(&level_template);
7708
7709   ActivateLevelTemplate();
7710 }
7711
7712 void LoadLevelTemplate(int nr)
7713 {
7714   if (!fileExists(getGlobalLevelTemplateFilename()))
7715   {
7716     Warn("no level template found for this level");
7717
7718     return;
7719   }
7720
7721   setLevelFileInfo(&level_template.file_info, nr);
7722
7723   LoadLevelTemplate_LoadAndInit();
7724 }
7725
7726 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7727 {
7728   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7729
7730   LoadLevelTemplate_LoadAndInit();
7731 }
7732
7733 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7734 {
7735   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7736
7737   if (level.use_custom_template)
7738   {
7739     if (network_level != NULL)
7740       LoadNetworkLevelTemplate(network_level);
7741     else
7742       LoadLevelTemplate(-1);
7743   }
7744
7745   LoadLevel_InitVersion(&level);
7746   LoadLevel_InitElements(&level);
7747   LoadLevel_InitPlayfield(&level);
7748   LoadLevel_InitSettings(&level);
7749
7750   LoadLevel_InitNativeEngines(&level);
7751 }
7752
7753 void LoadLevel(int nr)
7754 {
7755   SetLevelSetInfo(leveldir_current->identifier, nr);
7756
7757   setLevelFileInfo(&level.file_info, nr);
7758
7759   LoadLevel_LoadAndInit(NULL);
7760 }
7761
7762 void LoadLevelInfoOnly(int nr)
7763 {
7764   setLevelFileInfo(&level.file_info, nr);
7765
7766   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7767 }
7768
7769 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7770 {
7771   SetLevelSetInfo(network_level->leveldir_identifier,
7772                   network_level->file_info.nr);
7773
7774   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7775
7776   LoadLevel_LoadAndInit(network_level);
7777 }
7778
7779 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7780 {
7781   int chunk_size = 0;
7782
7783   chunk_size += putFileVersion(file, level->file_version);
7784   chunk_size += putFileVersion(file, level->game_version);
7785
7786   return chunk_size;
7787 }
7788
7789 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7790 {
7791   int chunk_size = 0;
7792
7793   chunk_size += putFile16BitBE(file, level->creation_date.year);
7794   chunk_size += putFile8Bit(file,    level->creation_date.month);
7795   chunk_size += putFile8Bit(file,    level->creation_date.day);
7796
7797   return chunk_size;
7798 }
7799
7800 #if ENABLE_HISTORIC_CHUNKS
7801 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7802 {
7803   int i, x, y;
7804
7805   putFile8Bit(file, level->fieldx);
7806   putFile8Bit(file, level->fieldy);
7807
7808   putFile16BitBE(file, level->time);
7809   putFile16BitBE(file, level->gems_needed);
7810
7811   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7812     putFile8Bit(file, level->name[i]);
7813
7814   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7815     putFile8Bit(file, level->score[i]);
7816
7817   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7818     for (y = 0; y < 3; y++)
7819       for (x = 0; x < 3; x++)
7820         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7821                            level->yamyam_content[i].e[x][y]));
7822   putFile8Bit(file, level->amoeba_speed);
7823   putFile8Bit(file, level->time_magic_wall);
7824   putFile8Bit(file, level->time_wheel);
7825   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7826                      level->amoeba_content));
7827   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7828   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7829   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7830   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7831
7832   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7833
7834   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7835   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7836   putFile32BitBE(file, level->can_move_into_acid_bits);
7837   putFile8Bit(file, level->dont_collide_with_bits);
7838
7839   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7840   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7841
7842   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7843   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7844   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7845
7846   putFile8Bit(file, level->game_engine_type);
7847
7848   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7849 }
7850 #endif
7851
7852 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7853 {
7854   int chunk_size = 0;
7855   int i;
7856
7857   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7858     chunk_size += putFile8Bit(file, level->name[i]);
7859
7860   return chunk_size;
7861 }
7862
7863 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7864 {
7865   int chunk_size = 0;
7866   int i;
7867
7868   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7869     chunk_size += putFile8Bit(file, level->author[i]);
7870
7871   return chunk_size;
7872 }
7873
7874 #if ENABLE_HISTORIC_CHUNKS
7875 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7876 {
7877   int chunk_size = 0;
7878   int x, y;
7879
7880   for (y = 0; y < level->fieldy; y++)
7881     for (x = 0; x < level->fieldx; x++)
7882       if (level->encoding_16bit_field)
7883         chunk_size += putFile16BitBE(file, level->field[x][y]);
7884       else
7885         chunk_size += putFile8Bit(file, level->field[x][y]);
7886
7887   return chunk_size;
7888 }
7889 #endif
7890
7891 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7892 {
7893   int chunk_size = 0;
7894   int x, y;
7895
7896   for (y = 0; y < level->fieldy; y++) 
7897     for (x = 0; x < level->fieldx; x++) 
7898       chunk_size += putFile16BitBE(file, level->field[x][y]);
7899
7900   return chunk_size;
7901 }
7902
7903 #if ENABLE_HISTORIC_CHUNKS
7904 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7905 {
7906   int i, x, y;
7907
7908   putFile8Bit(file, EL_YAMYAM);
7909   putFile8Bit(file, level->num_yamyam_contents);
7910   putFile8Bit(file, 0);
7911   putFile8Bit(file, 0);
7912
7913   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7914     for (y = 0; y < 3; y++)
7915       for (x = 0; x < 3; x++)
7916         if (level->encoding_16bit_field)
7917           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7918         else
7919           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7920 }
7921 #endif
7922
7923 #if ENABLE_HISTORIC_CHUNKS
7924 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7925 {
7926   int i, x, y;
7927   int num_contents, content_xsize, content_ysize;
7928   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7929
7930   if (element == EL_YAMYAM)
7931   {
7932     num_contents = level->num_yamyam_contents;
7933     content_xsize = 3;
7934     content_ysize = 3;
7935
7936     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7937       for (y = 0; y < 3; y++)
7938         for (x = 0; x < 3; x++)
7939           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7940   }
7941   else if (element == EL_BD_AMOEBA)
7942   {
7943     num_contents = 1;
7944     content_xsize = 1;
7945     content_ysize = 1;
7946
7947     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7948       for (y = 0; y < 3; y++)
7949         for (x = 0; x < 3; x++)
7950           content_array[i][x][y] = EL_EMPTY;
7951     content_array[0][0][0] = level->amoeba_content;
7952   }
7953   else
7954   {
7955     // chunk header already written -- write empty chunk data
7956     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7957
7958     Warn("cannot save content for element '%d'", element);
7959
7960     return;
7961   }
7962
7963   putFile16BitBE(file, element);
7964   putFile8Bit(file, num_contents);
7965   putFile8Bit(file, content_xsize);
7966   putFile8Bit(file, content_ysize);
7967
7968   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7969
7970   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7971     for (y = 0; y < 3; y++)
7972       for (x = 0; x < 3; x++)
7973         putFile16BitBE(file, content_array[i][x][y]);
7974 }
7975 #endif
7976
7977 #if ENABLE_HISTORIC_CHUNKS
7978 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7979 {
7980   int envelope_nr = element - EL_ENVELOPE_1;
7981   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7982   int chunk_size = 0;
7983   int i;
7984
7985   chunk_size += putFile16BitBE(file, element);
7986   chunk_size += putFile16BitBE(file, envelope_len);
7987   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7988   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7989
7990   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7991   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7992
7993   for (i = 0; i < envelope_len; i++)
7994     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7995
7996   return chunk_size;
7997 }
7998 #endif
7999
8000 #if ENABLE_HISTORIC_CHUNKS
8001 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8002                            int num_changed_custom_elements)
8003 {
8004   int i, check = 0;
8005
8006   putFile16BitBE(file, num_changed_custom_elements);
8007
8008   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8009   {
8010     int element = EL_CUSTOM_START + i;
8011
8012     struct ElementInfo *ei = &element_info[element];
8013
8014     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8015     {
8016       if (check < num_changed_custom_elements)
8017       {
8018         putFile16BitBE(file, element);
8019         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8020       }
8021
8022       check++;
8023     }
8024   }
8025
8026   if (check != num_changed_custom_elements)     // should not happen
8027     Warn("inconsistent number of custom element properties");
8028 }
8029 #endif
8030
8031 #if ENABLE_HISTORIC_CHUNKS
8032 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8033                            int num_changed_custom_elements)
8034 {
8035   int i, check = 0;
8036
8037   putFile16BitBE(file, num_changed_custom_elements);
8038
8039   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8040   {
8041     int element = EL_CUSTOM_START + i;
8042
8043     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8044     {
8045       if (check < num_changed_custom_elements)
8046       {
8047         putFile16BitBE(file, element);
8048         putFile16BitBE(file, element_info[element].change->target_element);
8049       }
8050
8051       check++;
8052     }
8053   }
8054
8055   if (check != num_changed_custom_elements)     // should not happen
8056     Warn("inconsistent number of custom target elements");
8057 }
8058 #endif
8059
8060 #if ENABLE_HISTORIC_CHUNKS
8061 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8062                            int num_changed_custom_elements)
8063 {
8064   int i, j, x, y, check = 0;
8065
8066   putFile16BitBE(file, num_changed_custom_elements);
8067
8068   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8069   {
8070     int element = EL_CUSTOM_START + i;
8071     struct ElementInfo *ei = &element_info[element];
8072
8073     if (ei->modified_settings)
8074     {
8075       if (check < num_changed_custom_elements)
8076       {
8077         putFile16BitBE(file, element);
8078
8079         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8080           putFile8Bit(file, ei->description[j]);
8081
8082         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8083
8084         // some free bytes for future properties and padding
8085         WriteUnusedBytesToFile(file, 7);
8086
8087         putFile8Bit(file, ei->use_gfx_element);
8088         putFile16BitBE(file, ei->gfx_element_initial);
8089
8090         putFile8Bit(file, ei->collect_score_initial);
8091         putFile8Bit(file, ei->collect_count_initial);
8092
8093         putFile16BitBE(file, ei->push_delay_fixed);
8094         putFile16BitBE(file, ei->push_delay_random);
8095         putFile16BitBE(file, ei->move_delay_fixed);
8096         putFile16BitBE(file, ei->move_delay_random);
8097
8098         putFile16BitBE(file, ei->move_pattern);
8099         putFile8Bit(file, ei->move_direction_initial);
8100         putFile8Bit(file, ei->move_stepsize);
8101
8102         for (y = 0; y < 3; y++)
8103           for (x = 0; x < 3; x++)
8104             putFile16BitBE(file, ei->content.e[x][y]);
8105
8106         putFile32BitBE(file, ei->change->events);
8107
8108         putFile16BitBE(file, ei->change->target_element);
8109
8110         putFile16BitBE(file, ei->change->delay_fixed);
8111         putFile16BitBE(file, ei->change->delay_random);
8112         putFile16BitBE(file, ei->change->delay_frames);
8113
8114         putFile16BitBE(file, ei->change->initial_trigger_element);
8115
8116         putFile8Bit(file, ei->change->explode);
8117         putFile8Bit(file, ei->change->use_target_content);
8118         putFile8Bit(file, ei->change->only_if_complete);
8119         putFile8Bit(file, ei->change->use_random_replace);
8120
8121         putFile8Bit(file, ei->change->random_percentage);
8122         putFile8Bit(file, ei->change->replace_when);
8123
8124         for (y = 0; y < 3; y++)
8125           for (x = 0; x < 3; x++)
8126             putFile16BitBE(file, ei->change->content.e[x][y]);
8127
8128         putFile8Bit(file, ei->slippery_type);
8129
8130         // some free bytes for future properties and padding
8131         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8132       }
8133
8134       check++;
8135     }
8136   }
8137
8138   if (check != num_changed_custom_elements)     // should not happen
8139     Warn("inconsistent number of custom element properties");
8140 }
8141 #endif
8142
8143 #if ENABLE_HISTORIC_CHUNKS
8144 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8145 {
8146   struct ElementInfo *ei = &element_info[element];
8147   int i, j, x, y;
8148
8149   // ---------- custom element base property values (96 bytes) ----------------
8150
8151   putFile16BitBE(file, element);
8152
8153   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8154     putFile8Bit(file, ei->description[i]);
8155
8156   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8157
8158   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8159
8160   putFile8Bit(file, ei->num_change_pages);
8161
8162   putFile16BitBE(file, ei->ce_value_fixed_initial);
8163   putFile16BitBE(file, ei->ce_value_random_initial);
8164   putFile8Bit(file, ei->use_last_ce_value);
8165
8166   putFile8Bit(file, ei->use_gfx_element);
8167   putFile16BitBE(file, ei->gfx_element_initial);
8168
8169   putFile8Bit(file, ei->collect_score_initial);
8170   putFile8Bit(file, ei->collect_count_initial);
8171
8172   putFile8Bit(file, ei->drop_delay_fixed);
8173   putFile8Bit(file, ei->push_delay_fixed);
8174   putFile8Bit(file, ei->drop_delay_random);
8175   putFile8Bit(file, ei->push_delay_random);
8176   putFile16BitBE(file, ei->move_delay_fixed);
8177   putFile16BitBE(file, ei->move_delay_random);
8178
8179   // bits 0 - 15 of "move_pattern" ...
8180   putFile16BitBE(file, ei->move_pattern & 0xffff);
8181   putFile8Bit(file, ei->move_direction_initial);
8182   putFile8Bit(file, ei->move_stepsize);
8183
8184   putFile8Bit(file, ei->slippery_type);
8185
8186   for (y = 0; y < 3; y++)
8187     for (x = 0; x < 3; x++)
8188       putFile16BitBE(file, ei->content.e[x][y]);
8189
8190   putFile16BitBE(file, ei->move_enter_element);
8191   putFile16BitBE(file, ei->move_leave_element);
8192   putFile8Bit(file, ei->move_leave_type);
8193
8194   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8195   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8196
8197   putFile8Bit(file, ei->access_direction);
8198
8199   putFile8Bit(file, ei->explosion_delay);
8200   putFile8Bit(file, ei->ignition_delay);
8201   putFile8Bit(file, ei->explosion_type);
8202
8203   // some free bytes for future custom property values and padding
8204   WriteUnusedBytesToFile(file, 1);
8205
8206   // ---------- change page property values (48 bytes) ------------------------
8207
8208   for (i = 0; i < ei->num_change_pages; i++)
8209   {
8210     struct ElementChangeInfo *change = &ei->change_page[i];
8211     unsigned int event_bits;
8212
8213     // bits 0 - 31 of "has_event[]" ...
8214     event_bits = 0;
8215     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8216       if (change->has_event[j])
8217         event_bits |= (1u << j);
8218     putFile32BitBE(file, event_bits);
8219
8220     putFile16BitBE(file, change->target_element);
8221
8222     putFile16BitBE(file, change->delay_fixed);
8223     putFile16BitBE(file, change->delay_random);
8224     putFile16BitBE(file, change->delay_frames);
8225
8226     putFile16BitBE(file, change->initial_trigger_element);
8227
8228     putFile8Bit(file, change->explode);
8229     putFile8Bit(file, change->use_target_content);
8230     putFile8Bit(file, change->only_if_complete);
8231     putFile8Bit(file, change->use_random_replace);
8232
8233     putFile8Bit(file, change->random_percentage);
8234     putFile8Bit(file, change->replace_when);
8235
8236     for (y = 0; y < 3; y++)
8237       for (x = 0; x < 3; x++)
8238         putFile16BitBE(file, change->target_content.e[x][y]);
8239
8240     putFile8Bit(file, change->can_change);
8241
8242     putFile8Bit(file, change->trigger_side);
8243
8244     putFile8Bit(file, change->trigger_player);
8245     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8246                        log_2(change->trigger_page)));
8247
8248     putFile8Bit(file, change->has_action);
8249     putFile8Bit(file, change->action_type);
8250     putFile8Bit(file, change->action_mode);
8251     putFile16BitBE(file, change->action_arg);
8252
8253     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8254     event_bits = 0;
8255     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8256       if (change->has_event[j])
8257         event_bits |= (1u << (j - 32));
8258     putFile8Bit(file, event_bits);
8259   }
8260 }
8261 #endif
8262
8263 #if ENABLE_HISTORIC_CHUNKS
8264 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8265 {
8266   struct ElementInfo *ei = &element_info[element];
8267   struct ElementGroupInfo *group = ei->group;
8268   int i;
8269
8270   putFile16BitBE(file, element);
8271
8272   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8273     putFile8Bit(file, ei->description[i]);
8274
8275   putFile8Bit(file, group->num_elements);
8276
8277   putFile8Bit(file, ei->use_gfx_element);
8278   putFile16BitBE(file, ei->gfx_element_initial);
8279
8280   putFile8Bit(file, group->choice_mode);
8281
8282   // some free bytes for future values and padding
8283   WriteUnusedBytesToFile(file, 3);
8284
8285   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8286     putFile16BitBE(file, group->element[i]);
8287 }
8288 #endif
8289
8290 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8291                                 boolean write_element)
8292 {
8293   int save_type = entry->save_type;
8294   int data_type = entry->data_type;
8295   int conf_type = entry->conf_type;
8296   int byte_mask = conf_type & CONF_MASK_BYTES;
8297   int element = entry->element;
8298   int default_value = entry->default_value;
8299   int num_bytes = 0;
8300   boolean modified = FALSE;
8301
8302   if (byte_mask != CONF_MASK_MULTI_BYTES)
8303   {
8304     void *value_ptr = entry->value;
8305     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8306                  *(int *)value_ptr);
8307
8308     // check if any settings have been modified before saving them
8309     if (value != default_value)
8310       modified = TRUE;
8311
8312     // do not save if explicitly told or if unmodified default settings
8313     if ((save_type == SAVE_CONF_NEVER) ||
8314         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8315       return 0;
8316
8317     if (write_element)
8318       num_bytes += putFile16BitBE(file, element);
8319
8320     num_bytes += putFile8Bit(file, conf_type);
8321     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8322                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8323                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8324                   0);
8325   }
8326   else if (data_type == TYPE_STRING)
8327   {
8328     char *default_string = entry->default_string;
8329     char *string = (char *)(entry->value);
8330     int string_length = strlen(string);
8331     int i;
8332
8333     // check if any settings have been modified before saving them
8334     if (!strEqual(string, default_string))
8335       modified = TRUE;
8336
8337     // do not save if explicitly told or if unmodified default settings
8338     if ((save_type == SAVE_CONF_NEVER) ||
8339         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8340       return 0;
8341
8342     if (write_element)
8343       num_bytes += putFile16BitBE(file, element);
8344
8345     num_bytes += putFile8Bit(file, conf_type);
8346     num_bytes += putFile16BitBE(file, string_length);
8347
8348     for (i = 0; i < string_length; i++)
8349       num_bytes += putFile8Bit(file, string[i]);
8350   }
8351   else if (data_type == TYPE_ELEMENT_LIST)
8352   {
8353     int *element_array = (int *)(entry->value);
8354     int num_elements = *(int *)(entry->num_entities);
8355     int i;
8356
8357     // check if any settings have been modified before saving them
8358     for (i = 0; i < num_elements; i++)
8359       if (element_array[i] != default_value)
8360         modified = TRUE;
8361
8362     // do not save if explicitly told or if unmodified default settings
8363     if ((save_type == SAVE_CONF_NEVER) ||
8364         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8365       return 0;
8366
8367     if (write_element)
8368       num_bytes += putFile16BitBE(file, element);
8369
8370     num_bytes += putFile8Bit(file, conf_type);
8371     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8372
8373     for (i = 0; i < num_elements; i++)
8374       num_bytes += putFile16BitBE(file, element_array[i]);
8375   }
8376   else if (data_type == TYPE_CONTENT_LIST)
8377   {
8378     struct Content *content = (struct Content *)(entry->value);
8379     int num_contents = *(int *)(entry->num_entities);
8380     int i, x, y;
8381
8382     // check if any settings have been modified before saving them
8383     for (i = 0; i < num_contents; i++)
8384       for (y = 0; y < 3; y++)
8385         for (x = 0; x < 3; x++)
8386           if (content[i].e[x][y] != default_value)
8387             modified = TRUE;
8388
8389     // do not save if explicitly told or if unmodified default settings
8390     if ((save_type == SAVE_CONF_NEVER) ||
8391         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8392       return 0;
8393
8394     if (write_element)
8395       num_bytes += putFile16BitBE(file, element);
8396
8397     num_bytes += putFile8Bit(file, conf_type);
8398     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8399
8400     for (i = 0; i < num_contents; i++)
8401       for (y = 0; y < 3; y++)
8402         for (x = 0; x < 3; x++)
8403           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8404   }
8405
8406   return num_bytes;
8407 }
8408
8409 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8410 {
8411   int chunk_size = 0;
8412   int i;
8413
8414   li = *level;          // copy level data into temporary buffer
8415
8416   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8417     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8418
8419   return chunk_size;
8420 }
8421
8422 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8423 {
8424   int chunk_size = 0;
8425   int i;
8426
8427   li = *level;          // copy level data into temporary buffer
8428
8429   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8430     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8431
8432   return chunk_size;
8433 }
8434
8435 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8436 {
8437   int envelope_nr = element - EL_ENVELOPE_1;
8438   int chunk_size = 0;
8439   int i;
8440
8441   chunk_size += putFile16BitBE(file, element);
8442
8443   // copy envelope data into temporary buffer
8444   xx_envelope = level->envelope[envelope_nr];
8445
8446   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8447     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8448
8449   return chunk_size;
8450 }
8451
8452 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8453 {
8454   struct ElementInfo *ei = &element_info[element];
8455   int chunk_size = 0;
8456   int i, j;
8457
8458   chunk_size += putFile16BitBE(file, element);
8459
8460   xx_ei = *ei;          // copy element data into temporary buffer
8461
8462   // set default description string for this specific element
8463   strcpy(xx_default_description, getDefaultElementDescription(ei));
8464
8465   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8466     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8467
8468   for (i = 0; i < ei->num_change_pages; i++)
8469   {
8470     struct ElementChangeInfo *change = &ei->change_page[i];
8471
8472     xx_current_change_page = i;
8473
8474     xx_change = *change;        // copy change data into temporary buffer
8475
8476     resetEventBits();
8477     setEventBitsFromEventFlags(change);
8478
8479     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8480       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8481                                          FALSE);
8482   }
8483
8484   return chunk_size;
8485 }
8486
8487 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8488 {
8489   struct ElementInfo *ei = &element_info[element];
8490   struct ElementGroupInfo *group = ei->group;
8491   int chunk_size = 0;
8492   int i;
8493
8494   chunk_size += putFile16BitBE(file, element);
8495
8496   xx_ei = *ei;          // copy element data into temporary buffer
8497   xx_group = *group;    // copy group data into temporary buffer
8498
8499   // set default description string for this specific element
8500   strcpy(xx_default_description, getDefaultElementDescription(ei));
8501
8502   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8503     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8504
8505   return chunk_size;
8506 }
8507
8508 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8509 {
8510   struct ElementInfo *ei = &element_info[element];
8511   int chunk_size = 0;
8512   int i;
8513
8514   chunk_size += putFile16BitBE(file, element);
8515
8516   xx_ei = *ei;          // copy element data into temporary buffer
8517
8518   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8519     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8520
8521   return chunk_size;
8522 }
8523
8524 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8525                                   boolean save_as_template)
8526 {
8527   int chunk_size;
8528   int i;
8529   FILE *file;
8530
8531   if (!(file = fopen(filename, MODE_WRITE)))
8532   {
8533     Warn("cannot save level file '%s'", filename);
8534
8535     return;
8536   }
8537
8538   level->file_version = FILE_VERSION_ACTUAL;
8539   level->game_version = GAME_VERSION_ACTUAL;
8540
8541   level->creation_date = getCurrentDate();
8542
8543   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8544   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8545
8546   chunk_size = SaveLevel_VERS(NULL, level);
8547   putFileChunkBE(file, "VERS", chunk_size);
8548   SaveLevel_VERS(file, level);
8549
8550   chunk_size = SaveLevel_DATE(NULL, level);
8551   putFileChunkBE(file, "DATE", chunk_size);
8552   SaveLevel_DATE(file, level);
8553
8554   chunk_size = SaveLevel_NAME(NULL, level);
8555   putFileChunkBE(file, "NAME", chunk_size);
8556   SaveLevel_NAME(file, level);
8557
8558   chunk_size = SaveLevel_AUTH(NULL, level);
8559   putFileChunkBE(file, "AUTH", chunk_size);
8560   SaveLevel_AUTH(file, level);
8561
8562   chunk_size = SaveLevel_INFO(NULL, level);
8563   putFileChunkBE(file, "INFO", chunk_size);
8564   SaveLevel_INFO(file, level);
8565
8566   chunk_size = SaveLevel_BODY(NULL, level);
8567   putFileChunkBE(file, "BODY", chunk_size);
8568   SaveLevel_BODY(file, level);
8569
8570   chunk_size = SaveLevel_ELEM(NULL, level);
8571   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8572   {
8573     putFileChunkBE(file, "ELEM", chunk_size);
8574     SaveLevel_ELEM(file, level);
8575   }
8576
8577   for (i = 0; i < NUM_ENVELOPES; i++)
8578   {
8579     int element = EL_ENVELOPE_1 + i;
8580
8581     chunk_size = SaveLevel_NOTE(NULL, level, element);
8582     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8583     {
8584       putFileChunkBE(file, "NOTE", chunk_size);
8585       SaveLevel_NOTE(file, level, element);
8586     }
8587   }
8588
8589   // if not using template level, check for non-default custom/group elements
8590   if (!level->use_custom_template || save_as_template)
8591   {
8592     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8593     {
8594       int element = EL_CUSTOM_START + i;
8595
8596       chunk_size = SaveLevel_CUSX(NULL, level, element);
8597       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8598       {
8599         putFileChunkBE(file, "CUSX", chunk_size);
8600         SaveLevel_CUSX(file, level, element);
8601       }
8602     }
8603
8604     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8605     {
8606       int element = EL_GROUP_START + i;
8607
8608       chunk_size = SaveLevel_GRPX(NULL, level, element);
8609       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8610       {
8611         putFileChunkBE(file, "GRPX", chunk_size);
8612         SaveLevel_GRPX(file, level, element);
8613       }
8614     }
8615
8616     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8617     {
8618       int element = GET_EMPTY_ELEMENT(i);
8619
8620       chunk_size = SaveLevel_EMPX(NULL, level, element);
8621       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8622       {
8623         putFileChunkBE(file, "EMPX", chunk_size);
8624         SaveLevel_EMPX(file, level, element);
8625       }
8626     }
8627   }
8628
8629   fclose(file);
8630
8631   SetFilePermissions(filename, PERMS_PRIVATE);
8632 }
8633
8634 void SaveLevel(int nr)
8635 {
8636   char *filename = getDefaultLevelFilename(nr);
8637
8638   SaveLevelFromFilename(&level, filename, FALSE);
8639 }
8640
8641 void SaveLevelTemplate(void)
8642 {
8643   char *filename = getLocalLevelTemplateFilename();
8644
8645   SaveLevelFromFilename(&level, filename, TRUE);
8646 }
8647
8648 boolean SaveLevelChecked(int nr)
8649 {
8650   char *filename = getDefaultLevelFilename(nr);
8651   boolean new_level = !fileExists(filename);
8652   boolean level_saved = FALSE;
8653
8654   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8655   {
8656     SaveLevel(nr);
8657
8658     if (new_level)
8659       Request("Level saved!", REQ_CONFIRM);
8660
8661     level_saved = TRUE;
8662   }
8663
8664   return level_saved;
8665 }
8666
8667 void DumpLevel(struct LevelInfo *level)
8668 {
8669   if (level->no_level_file || level->no_valid_file)
8670   {
8671     Warn("cannot dump -- no valid level file found");
8672
8673     return;
8674   }
8675
8676   PrintLine("-", 79);
8677   Print("Level xxx (file version %08d, game version %08d)\n",
8678         level->file_version, level->game_version);
8679   PrintLine("-", 79);
8680
8681   Print("Level author: '%s'\n", level->author);
8682   Print("Level title:  '%s'\n", level->name);
8683   Print("\n");
8684   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8685   Print("\n");
8686   Print("Level time:  %d seconds\n", level->time);
8687   Print("Gems needed: %d\n", level->gems_needed);
8688   Print("\n");
8689   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8690   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8691   Print("Time for light:      %d seconds\n", level->time_light);
8692   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8693   Print("\n");
8694   Print("Amoeba speed: %d\n", level->amoeba_speed);
8695   Print("\n");
8696
8697   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8698   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8699   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8700   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8701   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8702   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8703
8704   if (options.debug)
8705   {
8706     int i, j;
8707
8708     for (i = 0; i < NUM_ENVELOPES; i++)
8709     {
8710       char *text = level->envelope[i].text;
8711       int text_len = strlen(text);
8712       boolean has_text = FALSE;
8713
8714       for (j = 0; j < text_len; j++)
8715         if (text[j] != ' ' && text[j] != '\n')
8716           has_text = TRUE;
8717
8718       if (has_text)
8719       {
8720         Print("\n");
8721         Print("Envelope %d:\n'%s'\n", i + 1, text);
8722       }
8723     }
8724   }
8725
8726   PrintLine("-", 79);
8727 }
8728
8729 void DumpLevels(void)
8730 {
8731   static LevelDirTree *dumplevel_leveldir = NULL;
8732
8733   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8734                                                  global.dumplevel_leveldir);
8735
8736   if (dumplevel_leveldir == NULL)
8737     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8738
8739   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8740       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8741     Fail("no such level number: %d", global.dumplevel_level_nr);
8742
8743   leveldir_current = dumplevel_leveldir;
8744
8745   LoadLevel(global.dumplevel_level_nr);
8746   DumpLevel(&level);
8747
8748   CloseAllAndExit(0);
8749 }
8750
8751
8752 // ============================================================================
8753 // tape file functions
8754 // ============================================================================
8755
8756 static void setTapeInfoToDefaults(void)
8757 {
8758   int i;
8759
8760   // always start with reliable default values (empty tape)
8761   TapeErase();
8762
8763   // default values (also for pre-1.2 tapes) with only the first player
8764   tape.player_participates[0] = TRUE;
8765   for (i = 1; i < MAX_PLAYERS; i++)
8766     tape.player_participates[i] = FALSE;
8767
8768   // at least one (default: the first) player participates in every tape
8769   tape.num_participating_players = 1;
8770
8771   tape.property_bits = TAPE_PROPERTY_NONE;
8772
8773   tape.level_nr = level_nr;
8774   tape.counter = 0;
8775   tape.changed = FALSE;
8776   tape.solved = FALSE;
8777
8778   tape.recording = FALSE;
8779   tape.playing = FALSE;
8780   tape.pausing = FALSE;
8781
8782   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8783   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8784
8785   tape.no_info_chunk = TRUE;
8786   tape.no_valid_file = FALSE;
8787 }
8788
8789 static int getTapePosSize(struct TapeInfo *tape)
8790 {
8791   int tape_pos_size = 0;
8792
8793   if (tape->use_key_actions)
8794     tape_pos_size += tape->num_participating_players;
8795
8796   if (tape->use_mouse_actions)
8797     tape_pos_size += 3;         // x and y position and mouse button mask
8798
8799   tape_pos_size += 1;           // tape action delay value
8800
8801   return tape_pos_size;
8802 }
8803
8804 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8805 {
8806   tape->use_key_actions = FALSE;
8807   tape->use_mouse_actions = FALSE;
8808
8809   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8810     tape->use_key_actions = TRUE;
8811
8812   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8813     tape->use_mouse_actions = TRUE;
8814 }
8815
8816 static int getTapeActionValue(struct TapeInfo *tape)
8817 {
8818   return (tape->use_key_actions &&
8819           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8820           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8821           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8822           TAPE_ACTIONS_DEFAULT);
8823 }
8824
8825 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8826 {
8827   tape->file_version = getFileVersion(file);
8828   tape->game_version = getFileVersion(file);
8829
8830   return chunk_size;
8831 }
8832
8833 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8834 {
8835   int i;
8836
8837   tape->random_seed = getFile32BitBE(file);
8838   tape->date        = getFile32BitBE(file);
8839   tape->length      = getFile32BitBE(file);
8840
8841   // read header fields that are new since version 1.2
8842   if (tape->file_version >= FILE_VERSION_1_2)
8843   {
8844     byte store_participating_players = getFile8Bit(file);
8845     int engine_version;
8846
8847     // since version 1.2, tapes store which players participate in the tape
8848     tape->num_participating_players = 0;
8849     for (i = 0; i < MAX_PLAYERS; i++)
8850     {
8851       tape->player_participates[i] = FALSE;
8852
8853       if (store_participating_players & (1 << i))
8854       {
8855         tape->player_participates[i] = TRUE;
8856         tape->num_participating_players++;
8857       }
8858     }
8859
8860     setTapeActionFlags(tape, getFile8Bit(file));
8861
8862     tape->property_bits = getFile8Bit(file);
8863     tape->solved = getFile8Bit(file);
8864
8865     engine_version = getFileVersion(file);
8866     if (engine_version > 0)
8867       tape->engine_version = engine_version;
8868     else
8869       tape->engine_version = tape->game_version;
8870   }
8871
8872   return chunk_size;
8873 }
8874
8875 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8876 {
8877   tape->scr_fieldx = getFile8Bit(file);
8878   tape->scr_fieldy = getFile8Bit(file);
8879
8880   return chunk_size;
8881 }
8882
8883 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8884 {
8885   char *level_identifier = NULL;
8886   int level_identifier_size;
8887   int i;
8888
8889   tape->no_info_chunk = FALSE;
8890
8891   level_identifier_size = getFile16BitBE(file);
8892
8893   level_identifier = checked_malloc(level_identifier_size);
8894
8895   for (i = 0; i < level_identifier_size; i++)
8896     level_identifier[i] = getFile8Bit(file);
8897
8898   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8899   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8900
8901   checked_free(level_identifier);
8902
8903   tape->level_nr = getFile16BitBE(file);
8904
8905   chunk_size = 2 + level_identifier_size + 2;
8906
8907   return chunk_size;
8908 }
8909
8910 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8911 {
8912   int i, j;
8913   int tape_pos_size = getTapePosSize(tape);
8914   int chunk_size_expected = tape_pos_size * tape->length;
8915
8916   if (chunk_size_expected != chunk_size)
8917   {
8918     ReadUnusedBytesFromFile(file, chunk_size);
8919     return chunk_size_expected;
8920   }
8921
8922   for (i = 0; i < tape->length; i++)
8923   {
8924     if (i >= MAX_TAPE_LEN)
8925     {
8926       Warn("tape truncated -- size exceeds maximum tape size %d",
8927             MAX_TAPE_LEN);
8928
8929       // tape too large; read and ignore remaining tape data from this chunk
8930       for (;i < tape->length; i++)
8931         ReadUnusedBytesFromFile(file, tape_pos_size);
8932
8933       break;
8934     }
8935
8936     if (tape->use_key_actions)
8937     {
8938       for (j = 0; j < MAX_PLAYERS; j++)
8939       {
8940         tape->pos[i].action[j] = MV_NONE;
8941
8942         if (tape->player_participates[j])
8943           tape->pos[i].action[j] = getFile8Bit(file);
8944       }
8945     }
8946
8947     if (tape->use_mouse_actions)
8948     {
8949       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8950       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8951       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8952     }
8953
8954     tape->pos[i].delay = getFile8Bit(file);
8955
8956     if (tape->file_version == FILE_VERSION_1_0)
8957     {
8958       // eliminate possible diagonal moves in old tapes
8959       // this is only for backward compatibility
8960
8961       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8962       byte action = tape->pos[i].action[0];
8963       int k, num_moves = 0;
8964
8965       for (k = 0; k < 4; k++)
8966       {
8967         if (action & joy_dir[k])
8968         {
8969           tape->pos[i + num_moves].action[0] = joy_dir[k];
8970           if (num_moves > 0)
8971             tape->pos[i + num_moves].delay = 0;
8972           num_moves++;
8973         }
8974       }
8975
8976       if (num_moves > 1)
8977       {
8978         num_moves--;
8979         i += num_moves;
8980         tape->length += num_moves;
8981       }
8982     }
8983     else if (tape->file_version < FILE_VERSION_2_0)
8984     {
8985       // convert pre-2.0 tapes to new tape format
8986
8987       if (tape->pos[i].delay > 1)
8988       {
8989         // action part
8990         tape->pos[i + 1] = tape->pos[i];
8991         tape->pos[i + 1].delay = 1;
8992
8993         // delay part
8994         for (j = 0; j < MAX_PLAYERS; j++)
8995           tape->pos[i].action[j] = MV_NONE;
8996         tape->pos[i].delay--;
8997
8998         i++;
8999         tape->length++;
9000       }
9001     }
9002
9003     if (checkEndOfFile(file))
9004       break;
9005   }
9006
9007   if (i != tape->length)
9008     chunk_size = tape_pos_size * i;
9009
9010   return chunk_size;
9011 }
9012
9013 static void LoadTape_SokobanSolution(char *filename)
9014 {
9015   File *file;
9016   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9017
9018   if (!(file = openFile(filename, MODE_READ)))
9019   {
9020     tape.no_valid_file = TRUE;
9021
9022     return;
9023   }
9024
9025   while (!checkEndOfFile(file))
9026   {
9027     unsigned char c = getByteFromFile(file);
9028
9029     if (checkEndOfFile(file))
9030       break;
9031
9032     switch (c)
9033     {
9034       case 'u':
9035       case 'U':
9036         tape.pos[tape.length].action[0] = MV_UP;
9037         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9038         tape.length++;
9039         break;
9040
9041       case 'd':
9042       case 'D':
9043         tape.pos[tape.length].action[0] = MV_DOWN;
9044         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9045         tape.length++;
9046         break;
9047
9048       case 'l':
9049       case 'L':
9050         tape.pos[tape.length].action[0] = MV_LEFT;
9051         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9052         tape.length++;
9053         break;
9054
9055       case 'r':
9056       case 'R':
9057         tape.pos[tape.length].action[0] = MV_RIGHT;
9058         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9059         tape.length++;
9060         break;
9061
9062       case '\n':
9063       case '\r':
9064       case '\t':
9065       case ' ':
9066         // ignore white-space characters
9067         break;
9068
9069       default:
9070         tape.no_valid_file = TRUE;
9071
9072         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9073
9074         break;
9075     }
9076   }
9077
9078   closeFile(file);
9079
9080   if (tape.no_valid_file)
9081     return;
9082
9083   tape.length_frames  = GetTapeLengthFrames();
9084   tape.length_seconds = GetTapeLengthSeconds();
9085 }
9086
9087 void LoadTapeFromFilename(char *filename)
9088 {
9089   char cookie[MAX_LINE_LEN];
9090   char chunk_name[CHUNK_ID_LEN + 1];
9091   File *file;
9092   int chunk_size;
9093
9094   // always start with reliable default values
9095   setTapeInfoToDefaults();
9096
9097   if (strSuffix(filename, ".sln"))
9098   {
9099     LoadTape_SokobanSolution(filename);
9100
9101     return;
9102   }
9103
9104   if (!(file = openFile(filename, MODE_READ)))
9105   {
9106     tape.no_valid_file = TRUE;
9107
9108     return;
9109   }
9110
9111   getFileChunkBE(file, chunk_name, NULL);
9112   if (strEqual(chunk_name, "RND1"))
9113   {
9114     getFile32BitBE(file);               // not used
9115
9116     getFileChunkBE(file, chunk_name, NULL);
9117     if (!strEqual(chunk_name, "TAPE"))
9118     {
9119       tape.no_valid_file = TRUE;
9120
9121       Warn("unknown format of tape file '%s'", filename);
9122
9123       closeFile(file);
9124
9125       return;
9126     }
9127   }
9128   else  // check for pre-2.0 file format with cookie string
9129   {
9130     strcpy(cookie, chunk_name);
9131     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9132       cookie[4] = '\0';
9133     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9134       cookie[strlen(cookie) - 1] = '\0';
9135
9136     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9137     {
9138       tape.no_valid_file = TRUE;
9139
9140       Warn("unknown format of tape file '%s'", filename);
9141
9142       closeFile(file);
9143
9144       return;
9145     }
9146
9147     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9148     {
9149       tape.no_valid_file = TRUE;
9150
9151       Warn("unsupported version of tape file '%s'", filename);
9152
9153       closeFile(file);
9154
9155       return;
9156     }
9157
9158     // pre-2.0 tape files have no game version, so use file version here
9159     tape.game_version = tape.file_version;
9160   }
9161
9162   if (tape.file_version < FILE_VERSION_1_2)
9163   {
9164     // tape files from versions before 1.2.0 without chunk structure
9165     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9166     LoadTape_BODY(file, 2 * tape.length,      &tape);
9167   }
9168   else
9169   {
9170     static struct
9171     {
9172       char *name;
9173       int size;
9174       int (*loader)(File *, int, struct TapeInfo *);
9175     }
9176     chunk_info[] =
9177     {
9178       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9179       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9180       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9181       { "INFO", -1,                     LoadTape_INFO },
9182       { "BODY", -1,                     LoadTape_BODY },
9183       {  NULL,  0,                      NULL }
9184     };
9185
9186     while (getFileChunkBE(file, chunk_name, &chunk_size))
9187     {
9188       int i = 0;
9189
9190       while (chunk_info[i].name != NULL &&
9191              !strEqual(chunk_name, chunk_info[i].name))
9192         i++;
9193
9194       if (chunk_info[i].name == NULL)
9195       {
9196         Warn("unknown chunk '%s' in tape file '%s'",
9197               chunk_name, filename);
9198
9199         ReadUnusedBytesFromFile(file, chunk_size);
9200       }
9201       else if (chunk_info[i].size != -1 &&
9202                chunk_info[i].size != chunk_size)
9203       {
9204         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9205               chunk_size, chunk_name, filename);
9206
9207         ReadUnusedBytesFromFile(file, chunk_size);
9208       }
9209       else
9210       {
9211         // call function to load this tape chunk
9212         int chunk_size_expected =
9213           (chunk_info[i].loader)(file, chunk_size, &tape);
9214
9215         // the size of some chunks cannot be checked before reading other
9216         // chunks first (like "HEAD" and "BODY") that contain some header
9217         // information, so check them here
9218         if (chunk_size_expected != chunk_size)
9219         {
9220           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9221                 chunk_size, chunk_name, filename);
9222         }
9223       }
9224     }
9225   }
9226
9227   closeFile(file);
9228
9229   tape.length_frames  = GetTapeLengthFrames();
9230   tape.length_seconds = GetTapeLengthSeconds();
9231
9232 #if 0
9233   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9234         tape.file_version);
9235   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9236         tape.game_version);
9237   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9238         tape.engine_version);
9239 #endif
9240 }
9241
9242 void LoadTape(int nr)
9243 {
9244   char *filename = getTapeFilename(nr);
9245
9246   LoadTapeFromFilename(filename);
9247 }
9248
9249 void LoadSolutionTape(int nr)
9250 {
9251   char *filename = getSolutionTapeFilename(nr);
9252
9253   LoadTapeFromFilename(filename);
9254
9255   if (TAPE_IS_EMPTY(tape))
9256   {
9257     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9258         level.native_bd_level->replay != NULL)
9259       CopyNativeTape_BD_to_RND(&level);
9260     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9261         level.native_sp_level->demo.is_available)
9262       CopyNativeTape_SP_to_RND(&level);
9263   }
9264 }
9265
9266 void LoadScoreTape(char *score_tape_basename, int nr)
9267 {
9268   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9269
9270   LoadTapeFromFilename(filename);
9271 }
9272
9273 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9274 {
9275   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9276
9277   LoadTapeFromFilename(filename);
9278 }
9279
9280 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9281 {
9282   // chunk required for team mode tapes with non-default screen size
9283   return (tape->num_participating_players > 1 &&
9284           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9285            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9286 }
9287
9288 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9289 {
9290   putFileVersion(file, tape->file_version);
9291   putFileVersion(file, tape->game_version);
9292 }
9293
9294 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9295 {
9296   int i;
9297   byte store_participating_players = 0;
9298
9299   // set bits for participating players for compact storage
9300   for (i = 0; i < MAX_PLAYERS; i++)
9301     if (tape->player_participates[i])
9302       store_participating_players |= (1 << i);
9303
9304   putFile32BitBE(file, tape->random_seed);
9305   putFile32BitBE(file, tape->date);
9306   putFile32BitBE(file, tape->length);
9307
9308   putFile8Bit(file, store_participating_players);
9309
9310   putFile8Bit(file, getTapeActionValue(tape));
9311
9312   putFile8Bit(file, tape->property_bits);
9313   putFile8Bit(file, tape->solved);
9314
9315   putFileVersion(file, tape->engine_version);
9316 }
9317
9318 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9319 {
9320   putFile8Bit(file, tape->scr_fieldx);
9321   putFile8Bit(file, tape->scr_fieldy);
9322 }
9323
9324 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9325 {
9326   int level_identifier_size = strlen(tape->level_identifier) + 1;
9327   int i;
9328
9329   putFile16BitBE(file, level_identifier_size);
9330
9331   for (i = 0; i < level_identifier_size; i++)
9332     putFile8Bit(file, tape->level_identifier[i]);
9333
9334   putFile16BitBE(file, tape->level_nr);
9335 }
9336
9337 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9338 {
9339   int i, j;
9340
9341   for (i = 0; i < tape->length; i++)
9342   {
9343     if (tape->use_key_actions)
9344     {
9345       for (j = 0; j < MAX_PLAYERS; j++)
9346         if (tape->player_participates[j])
9347           putFile8Bit(file, tape->pos[i].action[j]);
9348     }
9349
9350     if (tape->use_mouse_actions)
9351     {
9352       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9353       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9354       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9355     }
9356
9357     putFile8Bit(file, tape->pos[i].delay);
9358   }
9359 }
9360
9361 void SaveTapeToFilename(char *filename)
9362 {
9363   FILE *file;
9364   int tape_pos_size;
9365   int info_chunk_size;
9366   int body_chunk_size;
9367
9368   if (!(file = fopen(filename, MODE_WRITE)))
9369   {
9370     Warn("cannot save level recording file '%s'", filename);
9371
9372     return;
9373   }
9374
9375   tape_pos_size = getTapePosSize(&tape);
9376
9377   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9378   body_chunk_size = tape_pos_size * tape.length;
9379
9380   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9381   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9382
9383   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9384   SaveTape_VERS(file, &tape);
9385
9386   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9387   SaveTape_HEAD(file, &tape);
9388
9389   if (checkSaveTape_SCRN(&tape))
9390   {
9391     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9392     SaveTape_SCRN(file, &tape);
9393   }
9394
9395   putFileChunkBE(file, "INFO", info_chunk_size);
9396   SaveTape_INFO(file, &tape);
9397
9398   putFileChunkBE(file, "BODY", body_chunk_size);
9399   SaveTape_BODY(file, &tape);
9400
9401   fclose(file);
9402
9403   SetFilePermissions(filename, PERMS_PRIVATE);
9404 }
9405
9406 static void SaveTapeExt(char *filename)
9407 {
9408   int i;
9409
9410   tape.file_version = FILE_VERSION_ACTUAL;
9411   tape.game_version = GAME_VERSION_ACTUAL;
9412
9413   tape.num_participating_players = 0;
9414
9415   // count number of participating players
9416   for (i = 0; i < MAX_PLAYERS; i++)
9417     if (tape.player_participates[i])
9418       tape.num_participating_players++;
9419
9420   SaveTapeToFilename(filename);
9421
9422   tape.changed = FALSE;
9423 }
9424
9425 void SaveTape(int nr)
9426 {
9427   char *filename = getTapeFilename(nr);
9428
9429   InitTapeDirectory(leveldir_current->subdir);
9430
9431   SaveTapeExt(filename);
9432 }
9433
9434 void SaveScoreTape(int nr)
9435 {
9436   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9437
9438   // used instead of "leveldir_current->subdir" (for network games)
9439   InitScoreTapeDirectory(levelset.identifier, nr);
9440
9441   SaveTapeExt(filename);
9442 }
9443
9444 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9445                                   unsigned int req_state_added)
9446 {
9447   char *filename = getTapeFilename(nr);
9448   boolean new_tape = !fileExists(filename);
9449   boolean tape_saved = FALSE;
9450
9451   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9452   {
9453     SaveTape(nr);
9454
9455     if (new_tape)
9456       Request(msg_saved, REQ_CONFIRM | req_state_added);
9457
9458     tape_saved = TRUE;
9459   }
9460
9461   return tape_saved;
9462 }
9463
9464 boolean SaveTapeChecked(int nr)
9465 {
9466   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9467 }
9468
9469 boolean SaveTapeChecked_LevelSolved(int nr)
9470 {
9471   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9472                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9473 }
9474
9475 void DumpTape(struct TapeInfo *tape)
9476 {
9477   int tape_frame_counter;
9478   int i, j;
9479
9480   if (tape->no_valid_file)
9481   {
9482     Warn("cannot dump -- no valid tape file found");
9483
9484     return;
9485   }
9486
9487   PrintLine("-", 79);
9488
9489   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9490         tape->level_nr, tape->file_version, tape->game_version);
9491   Print("                  (effective engine version %08d)\n",
9492         tape->engine_version);
9493   Print("Level series identifier: '%s'\n", tape->level_identifier);
9494
9495   Print("Solution tape: %s\n",
9496         tape->solved ? "yes" :
9497         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9498
9499   Print("Special tape properties: ");
9500   if (tape->property_bits == TAPE_PROPERTY_NONE)
9501     Print("[none]");
9502   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9503     Print("[em_random_bug]");
9504   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9505     Print("[game_speed]");
9506   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9507     Print("[pause]");
9508   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9509     Print("[single_step]");
9510   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9511     Print("[snapshot]");
9512   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9513     Print("[replayed]");
9514   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9515     Print("[tas_keys]");
9516   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9517     Print("[small_graphics]");
9518   Print("\n");
9519
9520   int year2 = tape->date / 10000;
9521   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9522   int month_index_raw = (tape->date / 100) % 100;
9523   int month_index = month_index_raw % 12;       // prevent invalid index
9524   int month = month_index + 1;
9525   int day = tape->date % 100;
9526
9527   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9528
9529   PrintLine("-", 79);
9530
9531   tape_frame_counter = 0;
9532
9533   for (i = 0; i < tape->length; i++)
9534   {
9535     if (i >= MAX_TAPE_LEN)
9536       break;
9537
9538     Print("%04d: ", i);
9539
9540     for (j = 0; j < MAX_PLAYERS; j++)
9541     {
9542       if (tape->player_participates[j])
9543       {
9544         int action = tape->pos[i].action[j];
9545
9546         Print("%d:%02x ", j, action);
9547         Print("[%c%c%c%c|%c%c] - ",
9548               (action & JOY_LEFT ? '<' : ' '),
9549               (action & JOY_RIGHT ? '>' : ' '),
9550               (action & JOY_UP ? '^' : ' '),
9551               (action & JOY_DOWN ? 'v' : ' '),
9552               (action & JOY_BUTTON_1 ? '1' : ' '),
9553               (action & JOY_BUTTON_2 ? '2' : ' '));
9554       }
9555     }
9556
9557     Print("(%03d) ", tape->pos[i].delay);
9558     Print("[%05d]\n", tape_frame_counter);
9559
9560     tape_frame_counter += tape->pos[i].delay;
9561   }
9562
9563   PrintLine("-", 79);
9564 }
9565
9566 void DumpTapes(void)
9567 {
9568   static LevelDirTree *dumptape_leveldir = NULL;
9569
9570   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9571                                                 global.dumptape_leveldir);
9572
9573   if (dumptape_leveldir == NULL)
9574     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9575
9576   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9577       global.dumptape_level_nr > dumptape_leveldir->last_level)
9578     Fail("no such level number: %d", global.dumptape_level_nr);
9579
9580   leveldir_current = dumptape_leveldir;
9581
9582   if (options.mytapes)
9583     LoadTape(global.dumptape_level_nr);
9584   else
9585     LoadSolutionTape(global.dumptape_level_nr);
9586
9587   DumpTape(&tape);
9588
9589   CloseAllAndExit(0);
9590 }
9591
9592
9593 // ============================================================================
9594 // score file functions
9595 // ============================================================================
9596
9597 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9598 {
9599   int i;
9600
9601   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9602   {
9603     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9604     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9605     scores->entry[i].score = 0;
9606     scores->entry[i].time = 0;
9607
9608     scores->entry[i].id = -1;
9609     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9610     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9611     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9612     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9613     strcpy(scores->entry[i].country_code, "??");
9614   }
9615
9616   scores->num_entries = 0;
9617   scores->last_added = -1;
9618   scores->last_added_local = -1;
9619
9620   scores->updated = FALSE;
9621   scores->uploaded = FALSE;
9622   scores->tape_downloaded = FALSE;
9623   scores->force_last_added = FALSE;
9624
9625   // The following values are intentionally not reset here:
9626   // - last_level_nr
9627   // - last_entry_nr
9628   // - next_level_nr
9629   // - continue_playing
9630   // - continue_on_return
9631 }
9632
9633 static void setScoreInfoToDefaults(void)
9634 {
9635   setScoreInfoToDefaultsExt(&scores);
9636 }
9637
9638 static void setServerScoreInfoToDefaults(void)
9639 {
9640   setScoreInfoToDefaultsExt(&server_scores);
9641 }
9642
9643 static void LoadScore_OLD(int nr)
9644 {
9645   int i;
9646   char *filename = getScoreFilename(nr);
9647   char cookie[MAX_LINE_LEN];
9648   char line[MAX_LINE_LEN];
9649   char *line_ptr;
9650   FILE *file;
9651
9652   if (!(file = fopen(filename, MODE_READ)))
9653     return;
9654
9655   // check file identifier
9656   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9657     cookie[0] = '\0';
9658   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9659     cookie[strlen(cookie) - 1] = '\0';
9660
9661   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9662   {
9663     Warn("unknown format of score file '%s'", filename);
9664
9665     fclose(file);
9666
9667     return;
9668   }
9669
9670   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9671   {
9672     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9673       Warn("fscanf() failed; %s", strerror(errno));
9674
9675     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9676       line[0] = '\0';
9677
9678     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9679       line[strlen(line) - 1] = '\0';
9680
9681     for (line_ptr = line; *line_ptr; line_ptr++)
9682     {
9683       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9684       {
9685         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9686         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9687         break;
9688       }
9689     }
9690   }
9691
9692   fclose(file);
9693 }
9694
9695 static void ConvertScore_OLD(void)
9696 {
9697   // only convert score to time for levels that rate playing time over score
9698   if (!level.rate_time_over_score)
9699     return;
9700
9701   // convert old score to playing time for score-less levels (like Supaplex)
9702   int time_final_max = 999;
9703   int i;
9704
9705   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9706   {
9707     int score = scores.entry[i].score;
9708
9709     if (score > 0 && score < time_final_max)
9710       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9711   }
9712 }
9713
9714 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9715 {
9716   scores->file_version = getFileVersion(file);
9717   scores->game_version = getFileVersion(file);
9718
9719   return chunk_size;
9720 }
9721
9722 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9723 {
9724   char *level_identifier = NULL;
9725   int level_identifier_size;
9726   int i;
9727
9728   level_identifier_size = getFile16BitBE(file);
9729
9730   level_identifier = checked_malloc(level_identifier_size);
9731
9732   for (i = 0; i < level_identifier_size; i++)
9733     level_identifier[i] = getFile8Bit(file);
9734
9735   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9736   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9737
9738   checked_free(level_identifier);
9739
9740   scores->level_nr = getFile16BitBE(file);
9741   scores->num_entries = getFile16BitBE(file);
9742
9743   chunk_size = 2 + level_identifier_size + 2 + 2;
9744
9745   return chunk_size;
9746 }
9747
9748 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9749 {
9750   int i, j;
9751
9752   for (i = 0; i < scores->num_entries; i++)
9753   {
9754     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9755       scores->entry[i].name[j] = getFile8Bit(file);
9756
9757     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9758   }
9759
9760   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9761
9762   return chunk_size;
9763 }
9764
9765 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9766 {
9767   int i;
9768
9769   for (i = 0; i < scores->num_entries; i++)
9770     scores->entry[i].score = getFile16BitBE(file);
9771
9772   chunk_size = scores->num_entries * 2;
9773
9774   return chunk_size;
9775 }
9776
9777 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9778 {
9779   int i;
9780
9781   for (i = 0; i < scores->num_entries; i++)
9782     scores->entry[i].score = getFile32BitBE(file);
9783
9784   chunk_size = scores->num_entries * 4;
9785
9786   return chunk_size;
9787 }
9788
9789 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9790 {
9791   int i;
9792
9793   for (i = 0; i < scores->num_entries; i++)
9794     scores->entry[i].time = getFile32BitBE(file);
9795
9796   chunk_size = scores->num_entries * 4;
9797
9798   return chunk_size;
9799 }
9800
9801 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9802 {
9803   int i, j;
9804
9805   for (i = 0; i < scores->num_entries; i++)
9806   {
9807     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9808       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9809
9810     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9811   }
9812
9813   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9814
9815   return chunk_size;
9816 }
9817
9818 void LoadScore(int nr)
9819 {
9820   char *filename = getScoreFilename(nr);
9821   char cookie[MAX_LINE_LEN];
9822   char chunk_name[CHUNK_ID_LEN + 1];
9823   int chunk_size;
9824   boolean old_score_file_format = FALSE;
9825   File *file;
9826
9827   // always start with reliable default values
9828   setScoreInfoToDefaults();
9829
9830   if (!(file = openFile(filename, MODE_READ)))
9831     return;
9832
9833   getFileChunkBE(file, chunk_name, NULL);
9834   if (strEqual(chunk_name, "RND1"))
9835   {
9836     getFile32BitBE(file);               // not used
9837
9838     getFileChunkBE(file, chunk_name, NULL);
9839     if (!strEqual(chunk_name, "SCOR"))
9840     {
9841       Warn("unknown format of score file '%s'", filename);
9842
9843       closeFile(file);
9844
9845       return;
9846     }
9847   }
9848   else  // check for old file format with cookie string
9849   {
9850     strcpy(cookie, chunk_name);
9851     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9852       cookie[4] = '\0';
9853     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9854       cookie[strlen(cookie) - 1] = '\0';
9855
9856     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9857     {
9858       Warn("unknown format of score file '%s'", filename);
9859
9860       closeFile(file);
9861
9862       return;
9863     }
9864
9865     old_score_file_format = TRUE;
9866   }
9867
9868   if (old_score_file_format)
9869   {
9870     // score files from versions before 4.2.4.0 without chunk structure
9871     LoadScore_OLD(nr);
9872
9873     // convert score to time, if possible (mainly for Supaplex levels)
9874     ConvertScore_OLD();
9875   }
9876   else
9877   {
9878     static struct
9879     {
9880       char *name;
9881       int size;
9882       int (*loader)(File *, int, struct ScoreInfo *);
9883     }
9884     chunk_info[] =
9885     {
9886       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9887       { "INFO", -1,                     LoadScore_INFO },
9888       { "NAME", -1,                     LoadScore_NAME },
9889       { "SCOR", -1,                     LoadScore_SCOR },
9890       { "SC4R", -1,                     LoadScore_SC4R },
9891       { "TIME", -1,                     LoadScore_TIME },
9892       { "TAPE", -1,                     LoadScore_TAPE },
9893
9894       {  NULL,  0,                      NULL }
9895     };
9896
9897     while (getFileChunkBE(file, chunk_name, &chunk_size))
9898     {
9899       int i = 0;
9900
9901       while (chunk_info[i].name != NULL &&
9902              !strEqual(chunk_name, chunk_info[i].name))
9903         i++;
9904
9905       if (chunk_info[i].name == NULL)
9906       {
9907         Warn("unknown chunk '%s' in score file '%s'",
9908               chunk_name, filename);
9909
9910         ReadUnusedBytesFromFile(file, chunk_size);
9911       }
9912       else if (chunk_info[i].size != -1 &&
9913                chunk_info[i].size != chunk_size)
9914       {
9915         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9916               chunk_size, chunk_name, filename);
9917
9918         ReadUnusedBytesFromFile(file, chunk_size);
9919       }
9920       else
9921       {
9922         // call function to load this score chunk
9923         int chunk_size_expected =
9924           (chunk_info[i].loader)(file, chunk_size, &scores);
9925
9926         // the size of some chunks cannot be checked before reading other
9927         // chunks first (like "HEAD" and "BODY") that contain some header
9928         // information, so check them here
9929         if (chunk_size_expected != chunk_size)
9930         {
9931           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9932                 chunk_size, chunk_name, filename);
9933         }
9934       }
9935     }
9936   }
9937
9938   closeFile(file);
9939 }
9940
9941 #if ENABLE_HISTORIC_CHUNKS
9942 void SaveScore_OLD(int nr)
9943 {
9944   int i;
9945   char *filename = getScoreFilename(nr);
9946   FILE *file;
9947
9948   // used instead of "leveldir_current->subdir" (for network games)
9949   InitScoreDirectory(levelset.identifier);
9950
9951   if (!(file = fopen(filename, MODE_WRITE)))
9952   {
9953     Warn("cannot save score for level %d", nr);
9954
9955     return;
9956   }
9957
9958   fprintf(file, "%s\n\n", SCORE_COOKIE);
9959
9960   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9961     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9962
9963   fclose(file);
9964
9965   SetFilePermissions(filename, PERMS_PRIVATE);
9966 }
9967 #endif
9968
9969 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9970 {
9971   putFileVersion(file, scores->file_version);
9972   putFileVersion(file, scores->game_version);
9973 }
9974
9975 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9976 {
9977   int level_identifier_size = strlen(scores->level_identifier) + 1;
9978   int i;
9979
9980   putFile16BitBE(file, level_identifier_size);
9981
9982   for (i = 0; i < level_identifier_size; i++)
9983     putFile8Bit(file, scores->level_identifier[i]);
9984
9985   putFile16BitBE(file, scores->level_nr);
9986   putFile16BitBE(file, scores->num_entries);
9987 }
9988
9989 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9990 {
9991   int i, j;
9992
9993   for (i = 0; i < scores->num_entries; i++)
9994   {
9995     int name_size = strlen(scores->entry[i].name);
9996
9997     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9998       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9999   }
10000 }
10001
10002 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10003 {
10004   int i;
10005
10006   for (i = 0; i < scores->num_entries; i++)
10007     putFile16BitBE(file, scores->entry[i].score);
10008 }
10009
10010 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10011 {
10012   int i;
10013
10014   for (i = 0; i < scores->num_entries; i++)
10015     putFile32BitBE(file, scores->entry[i].score);
10016 }
10017
10018 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10019 {
10020   int i;
10021
10022   for (i = 0; i < scores->num_entries; i++)
10023     putFile32BitBE(file, scores->entry[i].time);
10024 }
10025
10026 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10027 {
10028   int i, j;
10029
10030   for (i = 0; i < scores->num_entries; i++)
10031   {
10032     int size = strlen(scores->entry[i].tape_basename);
10033
10034     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10035       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10036   }
10037 }
10038
10039 static void SaveScoreToFilename(char *filename)
10040 {
10041   FILE *file;
10042   int info_chunk_size;
10043   int name_chunk_size;
10044   int scor_chunk_size;
10045   int sc4r_chunk_size;
10046   int time_chunk_size;
10047   int tape_chunk_size;
10048   boolean has_large_score_values;
10049   int i;
10050
10051   if (!(file = fopen(filename, MODE_WRITE)))
10052   {
10053     Warn("cannot save score file '%s'", filename);
10054
10055     return;
10056   }
10057
10058   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10059   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10060   scor_chunk_size = scores.num_entries * 2;
10061   sc4r_chunk_size = scores.num_entries * 4;
10062   time_chunk_size = scores.num_entries * 4;
10063   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10064
10065   has_large_score_values = FALSE;
10066   for (i = 0; i < scores.num_entries; i++)
10067     if (scores.entry[i].score > 0xffff)
10068       has_large_score_values = TRUE;
10069
10070   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10071   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10072
10073   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10074   SaveScore_VERS(file, &scores);
10075
10076   putFileChunkBE(file, "INFO", info_chunk_size);
10077   SaveScore_INFO(file, &scores);
10078
10079   putFileChunkBE(file, "NAME", name_chunk_size);
10080   SaveScore_NAME(file, &scores);
10081
10082   if (has_large_score_values)
10083   {
10084     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10085     SaveScore_SC4R(file, &scores);
10086   }
10087   else
10088   {
10089     putFileChunkBE(file, "SCOR", scor_chunk_size);
10090     SaveScore_SCOR(file, &scores);
10091   }
10092
10093   putFileChunkBE(file, "TIME", time_chunk_size);
10094   SaveScore_TIME(file, &scores);
10095
10096   putFileChunkBE(file, "TAPE", tape_chunk_size);
10097   SaveScore_TAPE(file, &scores);
10098
10099   fclose(file);
10100
10101   SetFilePermissions(filename, PERMS_PRIVATE);
10102 }
10103
10104 void SaveScore(int nr)
10105 {
10106   char *filename = getScoreFilename(nr);
10107   int i;
10108
10109   // used instead of "leveldir_current->subdir" (for network games)
10110   InitScoreDirectory(levelset.identifier);
10111
10112   scores.file_version = FILE_VERSION_ACTUAL;
10113   scores.game_version = GAME_VERSION_ACTUAL;
10114
10115   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10116   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10117   scores.level_nr = level_nr;
10118
10119   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10120     if (scores.entry[i].score == 0 &&
10121         scores.entry[i].time == 0 &&
10122         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10123       break;
10124
10125   scores.num_entries = i;
10126
10127   if (scores.num_entries == 0)
10128     return;
10129
10130   SaveScoreToFilename(filename);
10131 }
10132
10133 static void LoadServerScoreFromCache(int nr)
10134 {
10135   struct ScoreEntry score_entry;
10136   struct
10137   {
10138     void *value;
10139     boolean is_string;
10140     int string_size;
10141   }
10142   score_mapping[] =
10143   {
10144     { &score_entry.score,               FALSE,  0                       },
10145     { &score_entry.time,                FALSE,  0                       },
10146     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10147     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10148     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10149     { &score_entry.id,                  FALSE,  0                       },
10150     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10151     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10152     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10153     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10154
10155     { NULL,                             FALSE,  0                       }
10156   };
10157   char *filename = getScoreCacheFilename(nr);
10158   SetupFileHash *score_hash = loadSetupFileHash(filename);
10159   int i, j;
10160
10161   server_scores.num_entries = 0;
10162
10163   if (score_hash == NULL)
10164     return;
10165
10166   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10167   {
10168     score_entry = server_scores.entry[i];
10169
10170     for (j = 0; score_mapping[j].value != NULL; j++)
10171     {
10172       char token[10];
10173
10174       sprintf(token, "%02d.%d", i, j);
10175
10176       char *value = getHashEntry(score_hash, token);
10177
10178       if (value == NULL)
10179         continue;
10180
10181       if (score_mapping[j].is_string)
10182       {
10183         char *score_value = (char *)score_mapping[j].value;
10184         int value_size = score_mapping[j].string_size;
10185
10186         strncpy(score_value, value, value_size);
10187         score_value[value_size] = '\0';
10188       }
10189       else
10190       {
10191         int *score_value = (int *)score_mapping[j].value;
10192
10193         *score_value = atoi(value);
10194       }
10195
10196       server_scores.num_entries = i + 1;
10197     }
10198
10199     server_scores.entry[i] = score_entry;
10200   }
10201
10202   freeSetupFileHash(score_hash);
10203 }
10204
10205 void LoadServerScore(int nr, boolean download_score)
10206 {
10207   if (!setup.use_api_server)
10208     return;
10209
10210   // always start with reliable default values
10211   setServerScoreInfoToDefaults();
10212
10213   // 1st step: load server scores from cache file (which may not exist)
10214   // (this should prevent reading it while the thread is writing to it)
10215   LoadServerScoreFromCache(nr);
10216
10217   if (download_score && runtime.use_api_server)
10218   {
10219     // 2nd step: download server scores from score server to cache file
10220     // (as thread, as it might time out if the server is not reachable)
10221     ApiGetScoreAsThread(nr);
10222   }
10223 }
10224
10225 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10226 {
10227   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10228
10229   // if score tape not uploaded, ask for uploading missing tapes later
10230   if (!setup.has_remaining_tapes)
10231     setup.ask_for_remaining_tapes = TRUE;
10232
10233   setup.provide_uploading_tapes = TRUE;
10234   setup.has_remaining_tapes = TRUE;
10235
10236   SaveSetup_ServerSetup();
10237 }
10238
10239 void SaveServerScore(int nr, boolean tape_saved)
10240 {
10241   if (!runtime.use_api_server)
10242   {
10243     PrepareScoreTapesForUpload(leveldir_current->subdir);
10244
10245     return;
10246   }
10247
10248   ApiAddScoreAsThread(nr, tape_saved, NULL);
10249 }
10250
10251 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10252                              char *score_tape_filename)
10253 {
10254   if (!runtime.use_api_server)
10255     return;
10256
10257   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10258 }
10259
10260 void LoadLocalAndServerScore(int nr, boolean download_score)
10261 {
10262   int last_added_local = scores.last_added_local;
10263   boolean force_last_added = scores.force_last_added;
10264
10265   // needed if only showing server scores
10266   setScoreInfoToDefaults();
10267
10268   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10269     LoadScore(nr);
10270
10271   // restore last added local score entry (before merging server scores)
10272   scores.last_added = scores.last_added_local = last_added_local;
10273
10274   if (setup.use_api_server &&
10275       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10276   {
10277     // load server scores from cache file and trigger update from server
10278     LoadServerScore(nr, download_score);
10279
10280     // merge local scores with scores from server
10281     MergeServerScore();
10282   }
10283
10284   if (force_last_added)
10285     scores.force_last_added = force_last_added;
10286 }
10287
10288
10289 // ============================================================================
10290 // setup file functions
10291 // ============================================================================
10292
10293 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10294
10295
10296 static struct TokenInfo global_setup_tokens[] =
10297 {
10298   {
10299     TYPE_STRING,
10300     &setup.player_name,                         "player_name"
10301   },
10302   {
10303     TYPE_SWITCH,
10304     &setup.multiple_users,                      "multiple_users"
10305   },
10306   {
10307     TYPE_SWITCH,
10308     &setup.sound,                               "sound"
10309   },
10310   {
10311     TYPE_SWITCH,
10312     &setup.sound_loops,                         "repeating_sound_loops"
10313   },
10314   {
10315     TYPE_SWITCH,
10316     &setup.sound_music,                         "background_music"
10317   },
10318   {
10319     TYPE_SWITCH,
10320     &setup.sound_simple,                        "simple_sound_effects"
10321   },
10322   {
10323     TYPE_SWITCH,
10324     &setup.toons,                               "toons"
10325   },
10326   {
10327     TYPE_SWITCH,
10328     &setup.global_animations,                   "global_animations"
10329   },
10330   {
10331     TYPE_SWITCH,
10332     &setup.scroll_delay,                        "scroll_delay"
10333   },
10334   {
10335     TYPE_SWITCH,
10336     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10337   },
10338   {
10339     TYPE_INTEGER,
10340     &setup.scroll_delay_value,                  "scroll_delay_value"
10341   },
10342   {
10343     TYPE_STRING,
10344     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10345   },
10346   {
10347     TYPE_INTEGER,
10348     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10349   },
10350   {
10351     TYPE_SWITCH,
10352     &setup.fade_screens,                        "fade_screens"
10353   },
10354   {
10355     TYPE_SWITCH,
10356     &setup.autorecord,                          "automatic_tape_recording"
10357   },
10358   {
10359     TYPE_SWITCH,
10360     &setup.autorecord_after_replay,             "autorecord_after_replay"
10361   },
10362   {
10363     TYPE_SWITCH,
10364     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10365   },
10366   {
10367     TYPE_SWITCH,
10368     &setup.show_titlescreen,                    "show_titlescreen"
10369   },
10370   {
10371     TYPE_SWITCH,
10372     &setup.quick_doors,                         "quick_doors"
10373   },
10374   {
10375     TYPE_SWITCH,
10376     &setup.team_mode,                           "team_mode"
10377   },
10378   {
10379     TYPE_SWITCH,
10380     &setup.handicap,                            "handicap"
10381   },
10382   {
10383     TYPE_SWITCH,
10384     &setup.skip_levels,                         "skip_levels"
10385   },
10386   {
10387     TYPE_SWITCH,
10388     &setup.increment_levels,                    "increment_levels"
10389   },
10390   {
10391     TYPE_SWITCH,
10392     &setup.auto_play_next_level,                "auto_play_next_level"
10393   },
10394   {
10395     TYPE_SWITCH,
10396     &setup.count_score_after_game,              "count_score_after_game"
10397   },
10398   {
10399     TYPE_SWITCH,
10400     &setup.show_scores_after_game,              "show_scores_after_game"
10401   },
10402   {
10403     TYPE_SWITCH,
10404     &setup.time_limit,                          "time_limit"
10405   },
10406   {
10407     TYPE_SWITCH,
10408     &setup.fullscreen,                          "fullscreen"
10409   },
10410   {
10411     TYPE_INTEGER,
10412     &setup.window_scaling_percent,              "window_scaling_percent"
10413   },
10414   {
10415     TYPE_STRING,
10416     &setup.window_scaling_quality,              "window_scaling_quality"
10417   },
10418   {
10419     TYPE_STRING,
10420     &setup.screen_rendering_mode,               "screen_rendering_mode"
10421   },
10422   {
10423     TYPE_STRING,
10424     &setup.vsync_mode,                          "vsync_mode"
10425   },
10426   {
10427     TYPE_SWITCH,
10428     &setup.ask_on_escape,                       "ask_on_escape"
10429   },
10430   {
10431     TYPE_SWITCH,
10432     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10433   },
10434   {
10435     TYPE_SWITCH,
10436     &setup.ask_on_game_over,                    "ask_on_game_over"
10437   },
10438   {
10439     TYPE_SWITCH,
10440     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10441   },
10442   {
10443     TYPE_SWITCH,
10444     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10445   },
10446   {
10447     TYPE_SWITCH,
10448     &setup.quick_switch,                        "quick_player_switch"
10449   },
10450   {
10451     TYPE_SWITCH,
10452     &setup.input_on_focus,                      "input_on_focus"
10453   },
10454   {
10455     TYPE_SWITCH,
10456     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10457   },
10458   {
10459     TYPE_SWITCH,
10460     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10461   },
10462   {
10463     TYPE_SWITCH,
10464     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10465   },
10466   {
10467     TYPE_SWITCH,
10468     &setup.game_speed_extended,                 "game_speed_extended"
10469   },
10470   {
10471     TYPE_INTEGER,
10472     &setup.game_frame_delay,                    "game_frame_delay"
10473   },
10474   {
10475     TYPE_SWITCH,
10476     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10477   },
10478   {
10479     TYPE_SWITCH,
10480     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10481   },
10482   {
10483     TYPE_SWITCH,
10484     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10485   },
10486   {
10487     TYPE_SWITCH3,
10488     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10489   },
10490   {
10491     TYPE_SWITCH,
10492     &setup.sp_show_border_elements,             "sp_show_border_elements"
10493   },
10494   {
10495     TYPE_SWITCH,
10496     &setup.small_game_graphics,                 "small_game_graphics"
10497   },
10498   {
10499     TYPE_SWITCH,
10500     &setup.show_load_save_buttons,              "show_load_save_buttons"
10501   },
10502   {
10503     TYPE_SWITCH,
10504     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10505   },
10506   {
10507     TYPE_STRING,
10508     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10509   },
10510   {
10511     TYPE_STRING,
10512     &setup.graphics_set,                        "graphics_set"
10513   },
10514   {
10515     TYPE_STRING,
10516     &setup.sounds_set,                          "sounds_set"
10517   },
10518   {
10519     TYPE_STRING,
10520     &setup.music_set,                           "music_set"
10521   },
10522   {
10523     TYPE_SWITCH3,
10524     &setup.override_level_graphics,             "override_level_graphics"
10525   },
10526   {
10527     TYPE_SWITCH3,
10528     &setup.override_level_sounds,               "override_level_sounds"
10529   },
10530   {
10531     TYPE_SWITCH3,
10532     &setup.override_level_music,                "override_level_music"
10533   },
10534   {
10535     TYPE_INTEGER,
10536     &setup.volume_simple,                       "volume_simple"
10537   },
10538   {
10539     TYPE_INTEGER,
10540     &setup.volume_loops,                        "volume_loops"
10541   },
10542   {
10543     TYPE_INTEGER,
10544     &setup.volume_music,                        "volume_music"
10545   },
10546   {
10547     TYPE_SWITCH,
10548     &setup.network_mode,                        "network_mode"
10549   },
10550   {
10551     TYPE_PLAYER,
10552     &setup.network_player_nr,                   "network_player"
10553   },
10554   {
10555     TYPE_STRING,
10556     &setup.network_server_hostname,             "network_server_hostname"
10557   },
10558   {
10559     TYPE_STRING,
10560     &setup.touch.control_type,                  "touch.control_type"
10561   },
10562   {
10563     TYPE_INTEGER,
10564     &setup.touch.move_distance,                 "touch.move_distance"
10565   },
10566   {
10567     TYPE_INTEGER,
10568     &setup.touch.drop_distance,                 "touch.drop_distance"
10569   },
10570   {
10571     TYPE_INTEGER,
10572     &setup.touch.transparency,                  "touch.transparency"
10573   },
10574   {
10575     TYPE_INTEGER,
10576     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10577   },
10578   {
10579     TYPE_INTEGER,
10580     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10581   },
10582   {
10583     TYPE_INTEGER,
10584     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10585   },
10586   {
10587     TYPE_INTEGER,
10588     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10589   },
10590   {
10591     TYPE_INTEGER,
10592     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10593   },
10594   {
10595     TYPE_INTEGER,
10596     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10597   },
10598   {
10599     TYPE_SWITCH,
10600     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10601   },
10602 };
10603
10604 static struct TokenInfo auto_setup_tokens[] =
10605 {
10606   {
10607     TYPE_INTEGER,
10608     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10609   },
10610 };
10611
10612 static struct TokenInfo server_setup_tokens[] =
10613 {
10614   {
10615     TYPE_STRING,
10616     &setup.player_uuid,                         "player_uuid"
10617   },
10618   {
10619     TYPE_INTEGER,
10620     &setup.player_version,                      "player_version"
10621   },
10622   {
10623     TYPE_SWITCH,
10624     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10625   },
10626   {
10627     TYPE_STRING,
10628     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10629   },
10630   {
10631     TYPE_STRING,
10632     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10633   },
10634   {
10635     TYPE_SWITCH,
10636     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10637   },
10638   {
10639     TYPE_SWITCH,
10640     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10641   },
10642   {
10643     TYPE_SWITCH,
10644     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10645   },
10646   {
10647     TYPE_SWITCH,
10648     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10649   },
10650   {
10651     TYPE_SWITCH,
10652     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10653   },
10654 };
10655
10656 static struct TokenInfo editor_setup_tokens[] =
10657 {
10658   {
10659     TYPE_SWITCH,
10660     &setup.editor.el_classic,                   "editor.el_classic"
10661   },
10662   {
10663     TYPE_SWITCH,
10664     &setup.editor.el_custom,                    "editor.el_custom"
10665   },
10666   {
10667     TYPE_SWITCH,
10668     &setup.editor.el_user_defined,              "editor.el_user_defined"
10669   },
10670   {
10671     TYPE_SWITCH,
10672     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10673   },
10674   {
10675     TYPE_SWITCH,
10676     &setup.editor.el_headlines,                 "editor.el_headlines"
10677   },
10678   {
10679     TYPE_SWITCH,
10680     &setup.editor.show_element_token,           "editor.show_element_token"
10681   },
10682   {
10683     TYPE_SWITCH,
10684     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10685   },
10686 };
10687
10688 static struct TokenInfo editor_cascade_setup_tokens[] =
10689 {
10690   {
10691     TYPE_SWITCH,
10692     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10693   },
10694   {
10695     TYPE_SWITCH,
10696     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10697   },
10698   {
10699     TYPE_SWITCH,
10700     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10701   },
10702   {
10703     TYPE_SWITCH,
10704     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10705   },
10706   {
10707     TYPE_SWITCH,
10708     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10709   },
10710   {
10711     TYPE_SWITCH,
10712     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10713   },
10714   {
10715     TYPE_SWITCH,
10716     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10717   },
10718   {
10719     TYPE_SWITCH,
10720     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10721   },
10722   {
10723     TYPE_SWITCH,
10724     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10725   },
10726   {
10727     TYPE_SWITCH,
10728     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10729   },
10730   {
10731     TYPE_SWITCH,
10732     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10733   },
10734   {
10735     TYPE_SWITCH,
10736     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10737   },
10738   {
10739     TYPE_SWITCH,
10740     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10741   },
10742   {
10743     TYPE_SWITCH,
10744     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10745   },
10746   {
10747     TYPE_SWITCH,
10748     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10749   },
10750   {
10751     TYPE_SWITCH,
10752     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10753   },
10754   {
10755     TYPE_SWITCH,
10756     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10757   },
10758   {
10759     TYPE_SWITCH,
10760     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10761   },
10762   {
10763     TYPE_SWITCH,
10764     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10765   },
10766   {
10767     TYPE_SWITCH,
10768     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10769   },
10770 };
10771
10772 static struct TokenInfo shortcut_setup_tokens[] =
10773 {
10774   {
10775     TYPE_KEY_X11,
10776     &setup.shortcut.save_game,                  "shortcut.save_game"
10777   },
10778   {
10779     TYPE_KEY_X11,
10780     &setup.shortcut.load_game,                  "shortcut.load_game"
10781   },
10782   {
10783     TYPE_KEY_X11,
10784     &setup.shortcut.restart_game,               "shortcut.restart_game"
10785   },
10786   {
10787     TYPE_KEY_X11,
10788     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10789   },
10790   {
10791     TYPE_KEY_X11,
10792     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10793   },
10794   {
10795     TYPE_KEY_X11,
10796     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10797   },
10798   {
10799     TYPE_KEY_X11,
10800     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10801   },
10802   {
10803     TYPE_KEY_X11,
10804     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10805   },
10806   {
10807     TYPE_KEY_X11,
10808     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10809   },
10810   {
10811     TYPE_KEY_X11,
10812     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10813   },
10814   {
10815     TYPE_KEY_X11,
10816     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10817   },
10818   {
10819     TYPE_KEY_X11,
10820     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10821   },
10822   {
10823     TYPE_KEY_X11,
10824     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10825   },
10826   {
10827     TYPE_KEY_X11,
10828     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10829   },
10830   {
10831     TYPE_KEY_X11,
10832     &setup.shortcut.tape_record,                "shortcut.tape_record"
10833   },
10834   {
10835     TYPE_KEY_X11,
10836     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10837   },
10838   {
10839     TYPE_KEY_X11,
10840     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10841   },
10842   {
10843     TYPE_KEY_X11,
10844     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10845   },
10846   {
10847     TYPE_KEY_X11,
10848     &setup.shortcut.sound_music,                "shortcut.sound_music"
10849   },
10850   {
10851     TYPE_KEY_X11,
10852     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10853   },
10854   {
10855     TYPE_KEY_X11,
10856     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10857   },
10858   {
10859     TYPE_KEY_X11,
10860     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10861   },
10862   {
10863     TYPE_KEY_X11,
10864     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10865   },
10866 };
10867
10868 static struct SetupInputInfo setup_input;
10869 static struct TokenInfo player_setup_tokens[] =
10870 {
10871   {
10872     TYPE_BOOLEAN,
10873     &setup_input.use_joystick,                  ".use_joystick"
10874   },
10875   {
10876     TYPE_STRING,
10877     &setup_input.joy.device_name,               ".joy.device_name"
10878   },
10879   {
10880     TYPE_INTEGER,
10881     &setup_input.joy.xleft,                     ".joy.xleft"
10882   },
10883   {
10884     TYPE_INTEGER,
10885     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10886   },
10887   {
10888     TYPE_INTEGER,
10889     &setup_input.joy.xright,                    ".joy.xright"
10890   },
10891   {
10892     TYPE_INTEGER,
10893     &setup_input.joy.yupper,                    ".joy.yupper"
10894   },
10895   {
10896     TYPE_INTEGER,
10897     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10898   },
10899   {
10900     TYPE_INTEGER,
10901     &setup_input.joy.ylower,                    ".joy.ylower"
10902   },
10903   {
10904     TYPE_INTEGER,
10905     &setup_input.joy.snap,                      ".joy.snap_field"
10906   },
10907   {
10908     TYPE_INTEGER,
10909     &setup_input.joy.drop,                      ".joy.place_bomb"
10910   },
10911   {
10912     TYPE_KEY_X11,
10913     &setup_input.key.left,                      ".key.move_left"
10914   },
10915   {
10916     TYPE_KEY_X11,
10917     &setup_input.key.right,                     ".key.move_right"
10918   },
10919   {
10920     TYPE_KEY_X11,
10921     &setup_input.key.up,                        ".key.move_up"
10922   },
10923   {
10924     TYPE_KEY_X11,
10925     &setup_input.key.down,                      ".key.move_down"
10926   },
10927   {
10928     TYPE_KEY_X11,
10929     &setup_input.key.snap,                      ".key.snap_field"
10930   },
10931   {
10932     TYPE_KEY_X11,
10933     &setup_input.key.drop,                      ".key.place_bomb"
10934   },
10935 };
10936
10937 static struct TokenInfo system_setup_tokens[] =
10938 {
10939   {
10940     TYPE_STRING,
10941     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10942   },
10943   {
10944     TYPE_STRING,
10945     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10946   },
10947   {
10948     TYPE_STRING,
10949     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10950   },
10951   {
10952     TYPE_INTEGER,
10953     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10954   },
10955 };
10956
10957 static struct TokenInfo internal_setup_tokens[] =
10958 {
10959   {
10960     TYPE_STRING,
10961     &setup.internal.program_title,              "program_title"
10962   },
10963   {
10964     TYPE_STRING,
10965     &setup.internal.program_version,            "program_version"
10966   },
10967   {
10968     TYPE_STRING,
10969     &setup.internal.program_author,             "program_author"
10970   },
10971   {
10972     TYPE_STRING,
10973     &setup.internal.program_email,              "program_email"
10974   },
10975   {
10976     TYPE_STRING,
10977     &setup.internal.program_website,            "program_website"
10978   },
10979   {
10980     TYPE_STRING,
10981     &setup.internal.program_copyright,          "program_copyright"
10982   },
10983   {
10984     TYPE_STRING,
10985     &setup.internal.program_company,            "program_company"
10986   },
10987   {
10988     TYPE_STRING,
10989     &setup.internal.program_icon_file,          "program_icon_file"
10990   },
10991   {
10992     TYPE_STRING,
10993     &setup.internal.default_graphics_set,       "default_graphics_set"
10994   },
10995   {
10996     TYPE_STRING,
10997     &setup.internal.default_sounds_set,         "default_sounds_set"
10998   },
10999   {
11000     TYPE_STRING,
11001     &setup.internal.default_music_set,          "default_music_set"
11002   },
11003   {
11004     TYPE_STRING,
11005     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11006   },
11007   {
11008     TYPE_STRING,
11009     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11010   },
11011   {
11012     TYPE_STRING,
11013     &setup.internal.fallback_music_file,        "fallback_music_file"
11014   },
11015   {
11016     TYPE_STRING,
11017     &setup.internal.default_level_series,       "default_level_series"
11018   },
11019   {
11020     TYPE_INTEGER,
11021     &setup.internal.default_window_width,       "default_window_width"
11022   },
11023   {
11024     TYPE_INTEGER,
11025     &setup.internal.default_window_height,      "default_window_height"
11026   },
11027   {
11028     TYPE_BOOLEAN,
11029     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11030   },
11031   {
11032     TYPE_BOOLEAN,
11033     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11034   },
11035   {
11036     TYPE_BOOLEAN,
11037     &setup.internal.create_user_levelset,       "create_user_levelset"
11038   },
11039   {
11040     TYPE_BOOLEAN,
11041     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11042   },
11043   {
11044     TYPE_BOOLEAN,
11045     &setup.internal.menu_game,                  "menu_game"
11046   },
11047   {
11048     TYPE_BOOLEAN,
11049     &setup.internal.menu_engines,               "menu_engines"
11050   },
11051   {
11052     TYPE_BOOLEAN,
11053     &setup.internal.menu_editor,                "menu_editor"
11054   },
11055   {
11056     TYPE_BOOLEAN,
11057     &setup.internal.menu_graphics,              "menu_graphics"
11058   },
11059   {
11060     TYPE_BOOLEAN,
11061     &setup.internal.menu_sound,                 "menu_sound"
11062   },
11063   {
11064     TYPE_BOOLEAN,
11065     &setup.internal.menu_artwork,               "menu_artwork"
11066   },
11067   {
11068     TYPE_BOOLEAN,
11069     &setup.internal.menu_input,                 "menu_input"
11070   },
11071   {
11072     TYPE_BOOLEAN,
11073     &setup.internal.menu_touch,                 "menu_touch"
11074   },
11075   {
11076     TYPE_BOOLEAN,
11077     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11078   },
11079   {
11080     TYPE_BOOLEAN,
11081     &setup.internal.menu_exit,                  "menu_exit"
11082   },
11083   {
11084     TYPE_BOOLEAN,
11085     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11086   },
11087   {
11088     TYPE_BOOLEAN,
11089     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11090   },
11091   {
11092     TYPE_BOOLEAN,
11093     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11094   },
11095   {
11096     TYPE_BOOLEAN,
11097     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11098   },
11099   {
11100     TYPE_BOOLEAN,
11101     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11102   },
11103   {
11104     TYPE_BOOLEAN,
11105     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11106   },
11107   {
11108     TYPE_BOOLEAN,
11109     &setup.internal.info_title,                 "info_title"
11110   },
11111   {
11112     TYPE_BOOLEAN,
11113     &setup.internal.info_elements,              "info_elements"
11114   },
11115   {
11116     TYPE_BOOLEAN,
11117     &setup.internal.info_music,                 "info_music"
11118   },
11119   {
11120     TYPE_BOOLEAN,
11121     &setup.internal.info_credits,               "info_credits"
11122   },
11123   {
11124     TYPE_BOOLEAN,
11125     &setup.internal.info_program,               "info_program"
11126   },
11127   {
11128     TYPE_BOOLEAN,
11129     &setup.internal.info_version,               "info_version"
11130   },
11131   {
11132     TYPE_BOOLEAN,
11133     &setup.internal.info_levelset,              "info_levelset"
11134   },
11135   {
11136     TYPE_BOOLEAN,
11137     &setup.internal.info_exit,                  "info_exit"
11138   },
11139 };
11140
11141 static struct TokenInfo debug_setup_tokens[] =
11142 {
11143   {
11144     TYPE_INTEGER,
11145     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11146   },
11147   {
11148     TYPE_INTEGER,
11149     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11150   },
11151   {
11152     TYPE_INTEGER,
11153     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11154   },
11155   {
11156     TYPE_INTEGER,
11157     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11158   },
11159   {
11160     TYPE_INTEGER,
11161     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11162   },
11163   {
11164     TYPE_INTEGER,
11165     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11166   },
11167   {
11168     TYPE_INTEGER,
11169     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11170   },
11171   {
11172     TYPE_INTEGER,
11173     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11174   },
11175   {
11176     TYPE_INTEGER,
11177     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11178   },
11179   {
11180     TYPE_INTEGER,
11181     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11182   },
11183   {
11184     TYPE_KEY_X11,
11185     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11186   },
11187   {
11188     TYPE_KEY_X11,
11189     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11190   },
11191   {
11192     TYPE_KEY_X11,
11193     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11194   },
11195   {
11196     TYPE_KEY_X11,
11197     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11198   },
11199   {
11200     TYPE_KEY_X11,
11201     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11202   },
11203   {
11204     TYPE_KEY_X11,
11205     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11206   },
11207   {
11208     TYPE_KEY_X11,
11209     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11210   },
11211   {
11212     TYPE_KEY_X11,
11213     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11214   },
11215   {
11216     TYPE_KEY_X11,
11217     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11218   },
11219   {
11220     TYPE_KEY_X11,
11221     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11222   },
11223   {
11224     TYPE_BOOLEAN,
11225     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11226   {
11227     TYPE_BOOLEAN,
11228     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11229   },
11230   {
11231     TYPE_BOOLEAN,
11232     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11233   },
11234   {
11235     TYPE_SWITCH3,
11236     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11237   },
11238   {
11239     TYPE_INTEGER,
11240     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11241   },
11242 };
11243
11244 static struct TokenInfo options_setup_tokens[] =
11245 {
11246   {
11247     TYPE_BOOLEAN,
11248     &setup.options.verbose,                     "options.verbose"
11249   },
11250   {
11251     TYPE_BOOLEAN,
11252     &setup.options.debug,                       "options.debug"
11253   },
11254   {
11255     TYPE_STRING,
11256     &setup.options.debug_mode,                  "options.debug_mode"
11257   },
11258 };
11259
11260 static void setSetupInfoToDefaults(struct SetupInfo *si)
11261 {
11262   int i;
11263
11264   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11265
11266   si->multiple_users = TRUE;
11267
11268   si->sound = TRUE;
11269   si->sound_loops = TRUE;
11270   si->sound_music = TRUE;
11271   si->sound_simple = TRUE;
11272   si->toons = TRUE;
11273   si->global_animations = TRUE;
11274   si->scroll_delay = TRUE;
11275   si->forced_scroll_delay = FALSE;
11276   si->scroll_delay_value = STD_SCROLL_DELAY;
11277   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11278   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11279   si->fade_screens = TRUE;
11280   si->autorecord = TRUE;
11281   si->autorecord_after_replay = TRUE;
11282   si->auto_pause_on_start = FALSE;
11283   si->show_titlescreen = TRUE;
11284   si->quick_doors = FALSE;
11285   si->team_mode = FALSE;
11286   si->handicap = TRUE;
11287   si->skip_levels = TRUE;
11288   si->increment_levels = TRUE;
11289   si->auto_play_next_level = TRUE;
11290   si->count_score_after_game = TRUE;
11291   si->show_scores_after_game = TRUE;
11292   si->time_limit = TRUE;
11293   si->fullscreen = FALSE;
11294   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11295   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11296   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11297   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11298   si->ask_on_escape = TRUE;
11299   si->ask_on_escape_editor = TRUE;
11300   si->ask_on_game_over = TRUE;
11301   si->ask_on_quit_game = TRUE;
11302   si->ask_on_quit_program = TRUE;
11303   si->quick_switch = FALSE;
11304   si->input_on_focus = FALSE;
11305   si->prefer_aga_graphics = TRUE;
11306   si->prefer_lowpass_sounds = FALSE;
11307   si->prefer_extra_panel_items = TRUE;
11308   si->game_speed_extended = FALSE;
11309   si->game_frame_delay = GAME_FRAME_DELAY;
11310   si->bd_skip_uncovering = FALSE;
11311   si->bd_skip_hatching = FALSE;
11312   si->bd_scroll_delay = TRUE;
11313   si->bd_smooth_movements = AUTO;
11314   si->sp_show_border_elements = FALSE;
11315   si->small_game_graphics = FALSE;
11316   si->show_load_save_buttons = FALSE;
11317   si->show_undo_redo_buttons = FALSE;
11318   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11319
11320   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11321   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11322   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11323
11324   si->override_level_graphics = FALSE;
11325   si->override_level_sounds = FALSE;
11326   si->override_level_music = FALSE;
11327
11328   si->volume_simple = 100;              // percent
11329   si->volume_loops = 100;               // percent
11330   si->volume_music = 100;               // percent
11331
11332   si->network_mode = FALSE;
11333   si->network_player_nr = 0;            // first player
11334   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11335
11336   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11337   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11338   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11339   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11340   si->touch.draw_outlined = TRUE;
11341   si->touch.draw_pressed = TRUE;
11342
11343   for (i = 0; i < 2; i++)
11344   {
11345     char *default_grid_button[6][2] =
11346     {
11347       { "      ", "  ^^  " },
11348       { "      ", "  ^^  " },
11349       { "      ", "<<  >>" },
11350       { "      ", "<<  >>" },
11351       { "111222", "  vv  " },
11352       { "111222", "  vv  " }
11353     };
11354     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11355     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11356     int min_xsize = MIN(6, grid_xsize);
11357     int min_ysize = MIN(6, grid_ysize);
11358     int startx = grid_xsize - min_xsize;
11359     int starty = grid_ysize - min_ysize;
11360     int x, y;
11361
11362     // virtual buttons grid can only be set to defaults if video is initialized
11363     // (this will be repeated if virtual buttons are not loaded from setup file)
11364     if (video.initialized)
11365     {
11366       si->touch.grid_xsize[i] = grid_xsize;
11367       si->touch.grid_ysize[i] = grid_ysize;
11368     }
11369     else
11370     {
11371       si->touch.grid_xsize[i] = -1;
11372       si->touch.grid_ysize[i] = -1;
11373     }
11374
11375     for (x = 0; x < MAX_GRID_XSIZE; x++)
11376       for (y = 0; y < MAX_GRID_YSIZE; y++)
11377         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11378
11379     for (x = 0; x < min_xsize; x++)
11380       for (y = 0; y < min_ysize; y++)
11381         si->touch.grid_button[i][x][starty + y] =
11382           default_grid_button[y][0][x];
11383
11384     for (x = 0; x < min_xsize; x++)
11385       for (y = 0; y < min_ysize; y++)
11386         si->touch.grid_button[i][startx + x][starty + y] =
11387           default_grid_button[y][1][x];
11388   }
11389
11390   si->touch.grid_initialized            = video.initialized;
11391
11392   si->touch.overlay_buttons             = FALSE;
11393
11394   si->editor.el_boulderdash             = TRUE;
11395   si->editor.el_boulderdash_native      = TRUE;
11396   si->editor.el_boulderdash_effects     = TRUE;
11397   si->editor.el_emerald_mine            = TRUE;
11398   si->editor.el_emerald_mine_club       = TRUE;
11399   si->editor.el_more                    = TRUE;
11400   si->editor.el_sokoban                 = TRUE;
11401   si->editor.el_supaplex                = TRUE;
11402   si->editor.el_diamond_caves           = TRUE;
11403   si->editor.el_dx_boulderdash          = TRUE;
11404
11405   si->editor.el_mirror_magic            = TRUE;
11406   si->editor.el_deflektor               = TRUE;
11407
11408   si->editor.el_chars                   = TRUE;
11409   si->editor.el_steel_chars             = TRUE;
11410
11411   si->editor.el_classic                 = TRUE;
11412   si->editor.el_custom                  = TRUE;
11413
11414   si->editor.el_user_defined            = FALSE;
11415   si->editor.el_dynamic                 = TRUE;
11416
11417   si->editor.el_headlines               = TRUE;
11418
11419   si->editor.show_element_token         = FALSE;
11420
11421   si->editor.show_read_only_warning     = TRUE;
11422
11423   si->editor.use_template_for_new_levels = TRUE;
11424
11425   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11426   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11427   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11428   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11429   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11430
11431   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11432   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11433   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11434   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11435   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11436
11437   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11438   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11439   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11440   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11441   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11442   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11443
11444   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11445   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11446   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11447
11448   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11449   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11450   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11451   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11452
11453   for (i = 0; i < MAX_PLAYERS; i++)
11454   {
11455     si->input[i].use_joystick = FALSE;
11456     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11457     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11458     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11459     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11460     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11461     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11462     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11463     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11464     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11465     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11466     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11467     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11468     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11469     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11470     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11471   }
11472
11473   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11474   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11475   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11476   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11477
11478   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11479   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11480   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11481   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11482   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11483   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11484   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11485
11486   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11487
11488   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11489   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11490   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11491
11492   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11493   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11494   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11495
11496   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11497   si->internal.choose_from_top_leveldir = FALSE;
11498   si->internal.show_scaling_in_title = TRUE;
11499   si->internal.create_user_levelset = TRUE;
11500   si->internal.info_screens_from_main = FALSE;
11501
11502   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11503   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11504
11505   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11506   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11507   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11508   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11509   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11510   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11511   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11512   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11513   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11514   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11515
11516   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11517   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11518   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11519   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11520   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11521   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11522   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11523   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11524   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11525   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11526
11527   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11528   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11529
11530   si->debug.show_frames_per_second = FALSE;
11531
11532   si->debug.xsn_mode = AUTO;
11533   si->debug.xsn_percent = 0;
11534
11535   si->options.verbose = FALSE;
11536   si->options.debug = FALSE;
11537   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11538
11539 #if defined(PLATFORM_ANDROID)
11540   si->fullscreen = TRUE;
11541   si->touch.overlay_buttons = TRUE;
11542 #endif
11543
11544   setHideSetupEntry(&setup.debug.xsn_mode);
11545 }
11546
11547 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11548 {
11549   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11550 }
11551
11552 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11553 {
11554   si->player_uuid = NULL;       // (will be set later)
11555   si->player_version = 1;       // (will be set later)
11556
11557   si->use_api_server = TRUE;
11558   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11559   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11560   si->ask_for_uploading_tapes = TRUE;
11561   si->ask_for_remaining_tapes = FALSE;
11562   si->provide_uploading_tapes = TRUE;
11563   si->ask_for_using_api_server = TRUE;
11564   si->has_remaining_tapes = FALSE;
11565 }
11566
11567 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11568 {
11569   si->editor_cascade.el_bd              = TRUE;
11570   si->editor_cascade.el_bd_native       = TRUE;
11571   si->editor_cascade.el_bd_effects      = FALSE;
11572   si->editor_cascade.el_em              = TRUE;
11573   si->editor_cascade.el_emc             = TRUE;
11574   si->editor_cascade.el_rnd             = TRUE;
11575   si->editor_cascade.el_sb              = TRUE;
11576   si->editor_cascade.el_sp              = TRUE;
11577   si->editor_cascade.el_dc              = TRUE;
11578   si->editor_cascade.el_dx              = TRUE;
11579
11580   si->editor_cascade.el_mm              = TRUE;
11581   si->editor_cascade.el_df              = TRUE;
11582
11583   si->editor_cascade.el_chars           = FALSE;
11584   si->editor_cascade.el_steel_chars     = FALSE;
11585   si->editor_cascade.el_ce              = FALSE;
11586   si->editor_cascade.el_ge              = FALSE;
11587   si->editor_cascade.el_es              = FALSE;
11588   si->editor_cascade.el_ref             = FALSE;
11589   si->editor_cascade.el_user            = FALSE;
11590   si->editor_cascade.el_dynamic         = FALSE;
11591 }
11592
11593 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11594
11595 static char *getHideSetupToken(void *setup_value)
11596 {
11597   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11598
11599   if (setup_value != NULL)
11600     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11601
11602   return hide_setup_token;
11603 }
11604
11605 void setHideSetupEntry(void *setup_value)
11606 {
11607   char *hide_setup_token = getHideSetupToken(setup_value);
11608
11609   if (hide_setup_hash == NULL)
11610     hide_setup_hash = newSetupFileHash();
11611
11612   if (setup_value != NULL)
11613     setHashEntry(hide_setup_hash, hide_setup_token, "");
11614 }
11615
11616 void removeHideSetupEntry(void *setup_value)
11617 {
11618   char *hide_setup_token = getHideSetupToken(setup_value);
11619
11620   if (setup_value != NULL)
11621     removeHashEntry(hide_setup_hash, hide_setup_token);
11622 }
11623
11624 boolean hideSetupEntry(void *setup_value)
11625 {
11626   char *hide_setup_token = getHideSetupToken(setup_value);
11627
11628   return (setup_value != NULL &&
11629           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11630 }
11631
11632 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11633                                       struct TokenInfo *token_info,
11634                                       int token_nr, char *token_text)
11635 {
11636   char *token_hide_text = getStringCat2(token_text, ".hide");
11637   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11638
11639   // set the value of this setup option in the setup option structure
11640   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11641
11642   // check if this setup option should be hidden in the setup menu
11643   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11644     setHideSetupEntry(token_info[token_nr].value);
11645
11646   free(token_hide_text);
11647 }
11648
11649 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11650                                       struct TokenInfo *token_info,
11651                                       int token_nr)
11652 {
11653   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11654                             token_info[token_nr].text);
11655 }
11656
11657 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11658 {
11659   int i, pnr;
11660
11661   if (!setup_file_hash)
11662     return;
11663
11664   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11665     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11666
11667   setup.touch.grid_initialized = TRUE;
11668   for (i = 0; i < 2; i++)
11669   {
11670     int grid_xsize = setup.touch.grid_xsize[i];
11671     int grid_ysize = setup.touch.grid_ysize[i];
11672     int x, y;
11673
11674     // if virtual buttons are not loaded from setup file, repeat initializing
11675     // virtual buttons grid with default values later when video is initialized
11676     if (grid_xsize == -1 ||
11677         grid_ysize == -1)
11678     {
11679       setup.touch.grid_initialized = FALSE;
11680
11681       continue;
11682     }
11683
11684     for (y = 0; y < grid_ysize; y++)
11685     {
11686       char token_string[MAX_LINE_LEN];
11687
11688       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11689
11690       char *value_string = getHashEntry(setup_file_hash, token_string);
11691
11692       if (value_string == NULL)
11693         continue;
11694
11695       for (x = 0; x < grid_xsize; x++)
11696       {
11697         char c = value_string[x];
11698
11699         setup.touch.grid_button[i][x][y] =
11700           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11701       }
11702     }
11703   }
11704
11705   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11706     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11707
11708   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11709     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11710
11711   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11712   {
11713     char prefix[30];
11714
11715     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11716
11717     setup_input = setup.input[pnr];
11718     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11719     {
11720       char full_token[100];
11721
11722       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11723       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11724                                 full_token);
11725     }
11726     setup.input[pnr] = setup_input;
11727   }
11728
11729   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11730     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11731
11732   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11733     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11734
11735   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11736     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11737
11738   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11739     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11740
11741   setHideRelatedSetupEntries();
11742 }
11743
11744 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11745 {
11746   int i;
11747
11748   if (!setup_file_hash)
11749     return;
11750
11751   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11752     setSetupInfo(auto_setup_tokens, i,
11753                  getHashEntry(setup_file_hash,
11754                               auto_setup_tokens[i].text));
11755 }
11756
11757 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11758 {
11759   int i;
11760
11761   if (!setup_file_hash)
11762     return;
11763
11764   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11765     setSetupInfo(server_setup_tokens, i,
11766                  getHashEntry(setup_file_hash,
11767                               server_setup_tokens[i].text));
11768 }
11769
11770 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11771 {
11772   int i;
11773
11774   if (!setup_file_hash)
11775     return;
11776
11777   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11778     setSetupInfo(editor_cascade_setup_tokens, i,
11779                  getHashEntry(setup_file_hash,
11780                               editor_cascade_setup_tokens[i].text));
11781 }
11782
11783 void LoadUserNames(void)
11784 {
11785   int last_user_nr = user.nr;
11786   int i;
11787
11788   if (global.user_names != NULL)
11789   {
11790     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11791       checked_free(global.user_names[i]);
11792
11793     checked_free(global.user_names);
11794   }
11795
11796   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11797
11798   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11799   {
11800     user.nr = i;
11801
11802     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11803
11804     if (setup_file_hash)
11805     {
11806       char *player_name = getHashEntry(setup_file_hash, "player_name");
11807
11808       global.user_names[i] = getFixedUserName(player_name);
11809
11810       freeSetupFileHash(setup_file_hash);
11811     }
11812
11813     if (global.user_names[i] == NULL)
11814       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11815   }
11816
11817   user.nr = last_user_nr;
11818 }
11819
11820 void LoadSetupFromFilename(char *filename)
11821 {
11822   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11823
11824   if (setup_file_hash)
11825   {
11826     decodeSetupFileHash_Default(setup_file_hash);
11827
11828     freeSetupFileHash(setup_file_hash);
11829   }
11830   else
11831   {
11832     Debug("setup", "using default setup values");
11833   }
11834 }
11835
11836 static void LoadSetup_SpecialPostProcessing(void)
11837 {
11838   char *player_name_new;
11839
11840   // needed to work around problems with fixed length strings
11841   player_name_new = getFixedUserName(setup.player_name);
11842   free(setup.player_name);
11843   setup.player_name = player_name_new;
11844
11845   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11846   if (setup.scroll_delay == FALSE)
11847   {
11848     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11849     setup.scroll_delay = TRUE;                  // now always "on"
11850   }
11851
11852   // make sure that scroll delay value stays inside valid range
11853   setup.scroll_delay_value =
11854     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11855 }
11856
11857 void LoadSetup_Default(void)
11858 {
11859   char *filename;
11860
11861   // always start with reliable default values
11862   setSetupInfoToDefaults(&setup);
11863
11864   // try to load setup values from default setup file
11865   filename = getDefaultSetupFilename();
11866
11867   if (fileExists(filename))
11868     LoadSetupFromFilename(filename);
11869
11870   // try to load setup values from platform setup file
11871   filename = getPlatformSetupFilename();
11872
11873   if (fileExists(filename))
11874     LoadSetupFromFilename(filename);
11875
11876   // try to load setup values from user setup file
11877   filename = getSetupFilename();
11878
11879   LoadSetupFromFilename(filename);
11880
11881   LoadSetup_SpecialPostProcessing();
11882 }
11883
11884 void LoadSetup_AutoSetup(void)
11885 {
11886   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11887   SetupFileHash *setup_file_hash = NULL;
11888
11889   // always start with reliable default values
11890   setSetupInfoToDefaults_AutoSetup(&setup);
11891
11892   setup_file_hash = loadSetupFileHash(filename);
11893
11894   if (setup_file_hash)
11895   {
11896     decodeSetupFileHash_AutoSetup(setup_file_hash);
11897
11898     freeSetupFileHash(setup_file_hash);
11899   }
11900
11901   free(filename);
11902 }
11903
11904 void LoadSetup_ServerSetup(void)
11905 {
11906   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11907   SetupFileHash *setup_file_hash = NULL;
11908
11909   // always start with reliable default values
11910   setSetupInfoToDefaults_ServerSetup(&setup);
11911
11912   setup_file_hash = loadSetupFileHash(filename);
11913
11914   if (setup_file_hash)
11915   {
11916     decodeSetupFileHash_ServerSetup(setup_file_hash);
11917
11918     freeSetupFileHash(setup_file_hash);
11919   }
11920
11921   free(filename);
11922
11923   if (setup.player_uuid == NULL)
11924   {
11925     // player UUID does not yet exist in setup file
11926     setup.player_uuid = getStringCopy(getUUID());
11927     setup.player_version = 2;
11928
11929     SaveSetup_ServerSetup();
11930   }
11931 }
11932
11933 void LoadSetup_EditorCascade(void)
11934 {
11935   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11936   SetupFileHash *setup_file_hash = NULL;
11937
11938   // always start with reliable default values
11939   setSetupInfoToDefaults_EditorCascade(&setup);
11940
11941   setup_file_hash = loadSetupFileHash(filename);
11942
11943   if (setup_file_hash)
11944   {
11945     decodeSetupFileHash_EditorCascade(setup_file_hash);
11946
11947     freeSetupFileHash(setup_file_hash);
11948   }
11949
11950   free(filename);
11951 }
11952
11953 void LoadSetup(void)
11954 {
11955   LoadSetup_Default();
11956   LoadSetup_AutoSetup();
11957   LoadSetup_ServerSetup();
11958   LoadSetup_EditorCascade();
11959 }
11960
11961 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11962                                            char *mapping_line)
11963 {
11964   char mapping_guid[MAX_LINE_LEN];
11965   char *mapping_start, *mapping_end;
11966
11967   // get GUID from game controller mapping line: copy complete line
11968   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11969   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11970
11971   // get GUID from game controller mapping line: cut after GUID part
11972   mapping_start = strchr(mapping_guid, ',');
11973   if (mapping_start != NULL)
11974     *mapping_start = '\0';
11975
11976   // cut newline from game controller mapping line
11977   mapping_end = strchr(mapping_line, '\n');
11978   if (mapping_end != NULL)
11979     *mapping_end = '\0';
11980
11981   // add mapping entry to game controller mappings hash
11982   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11983 }
11984
11985 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11986                                                  char *filename)
11987 {
11988   FILE *file;
11989
11990   if (!(file = fopen(filename, MODE_READ)))
11991   {
11992     Warn("cannot read game controller mappings file '%s'", filename);
11993
11994     return;
11995   }
11996
11997   while (!feof(file))
11998   {
11999     char line[MAX_LINE_LEN];
12000
12001     if (!fgets(line, MAX_LINE_LEN, file))
12002       break;
12003
12004     addGameControllerMappingToHash(mappings_hash, line);
12005   }
12006
12007   fclose(file);
12008 }
12009
12010 void SaveSetup_Default(void)
12011 {
12012   char *filename = getSetupFilename();
12013   FILE *file;
12014   int i, pnr;
12015
12016   InitUserDataDirectory();
12017
12018   if (!(file = fopen(filename, MODE_WRITE)))
12019   {
12020     Warn("cannot write setup file '%s'", filename);
12021
12022     return;
12023   }
12024
12025   fprintFileHeader(file, SETUP_FILENAME);
12026
12027   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12028   {
12029     // just to make things nicer :)
12030     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12031         global_setup_tokens[i].value == &setup.sound                    ||
12032         global_setup_tokens[i].value == &setup.graphics_set             ||
12033         global_setup_tokens[i].value == &setup.volume_simple            ||
12034         global_setup_tokens[i].value == &setup.network_mode             ||
12035         global_setup_tokens[i].value == &setup.touch.control_type       ||
12036         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12037         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12038       fprintf(file, "\n");
12039
12040     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12041   }
12042
12043   for (i = 0; i < 2; i++)
12044   {
12045     int grid_xsize = setup.touch.grid_xsize[i];
12046     int grid_ysize = setup.touch.grid_ysize[i];
12047     int x, y;
12048
12049     fprintf(file, "\n");
12050
12051     for (y = 0; y < grid_ysize; y++)
12052     {
12053       char token_string[MAX_LINE_LEN];
12054       char value_string[MAX_LINE_LEN];
12055
12056       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12057
12058       for (x = 0; x < grid_xsize; x++)
12059       {
12060         char c = setup.touch.grid_button[i][x][y];
12061
12062         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12063       }
12064
12065       value_string[grid_xsize] = '\0';
12066
12067       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12068     }
12069   }
12070
12071   fprintf(file, "\n");
12072   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12073     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12074
12075   fprintf(file, "\n");
12076   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12077     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12078
12079   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12080   {
12081     char prefix[30];
12082
12083     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12084     fprintf(file, "\n");
12085
12086     setup_input = setup.input[pnr];
12087     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12088       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12089   }
12090
12091   fprintf(file, "\n");
12092   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12093     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12094
12095   // (internal setup values not saved to user setup file)
12096
12097   fprintf(file, "\n");
12098   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12099     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12100         setup.debug.xsn_mode != AUTO)
12101       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12102
12103   fprintf(file, "\n");
12104   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12105     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12106
12107   fclose(file);
12108
12109   SetFilePermissions(filename, PERMS_PRIVATE);
12110 }
12111
12112 void SaveSetup_AutoSetup(void)
12113 {
12114   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12115   FILE *file;
12116   int i;
12117
12118   InitUserDataDirectory();
12119
12120   if (!(file = fopen(filename, MODE_WRITE)))
12121   {
12122     Warn("cannot write auto setup file '%s'", filename);
12123
12124     free(filename);
12125
12126     return;
12127   }
12128
12129   fprintFileHeader(file, AUTOSETUP_FILENAME);
12130
12131   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12132     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12133
12134   fclose(file);
12135
12136   SetFilePermissions(filename, PERMS_PRIVATE);
12137
12138   free(filename);
12139 }
12140
12141 void SaveSetup_ServerSetup(void)
12142 {
12143   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12144   FILE *file;
12145   int i;
12146
12147   InitUserDataDirectory();
12148
12149   if (!(file = fopen(filename, MODE_WRITE)))
12150   {
12151     Warn("cannot write server setup file '%s'", filename);
12152
12153     free(filename);
12154
12155     return;
12156   }
12157
12158   fprintFileHeader(file, SERVERSETUP_FILENAME);
12159
12160   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12161   {
12162     // just to make things nicer :)
12163     if (server_setup_tokens[i].value == &setup.use_api_server)
12164       fprintf(file, "\n");
12165
12166     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12167   }
12168
12169   fclose(file);
12170
12171   SetFilePermissions(filename, PERMS_PRIVATE);
12172
12173   free(filename);
12174 }
12175
12176 void SaveSetup_EditorCascade(void)
12177 {
12178   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12179   FILE *file;
12180   int i;
12181
12182   InitUserDataDirectory();
12183
12184   if (!(file = fopen(filename, MODE_WRITE)))
12185   {
12186     Warn("cannot write editor cascade state file '%s'", filename);
12187
12188     free(filename);
12189
12190     return;
12191   }
12192
12193   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12194
12195   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12196     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12197
12198   fclose(file);
12199
12200   SetFilePermissions(filename, PERMS_PRIVATE);
12201
12202   free(filename);
12203 }
12204
12205 void SaveSetup(void)
12206 {
12207   SaveSetup_Default();
12208   SaveSetup_AutoSetup();
12209   SaveSetup_ServerSetup();
12210   SaveSetup_EditorCascade();
12211 }
12212
12213 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12214                                                   char *filename)
12215 {
12216   FILE *file;
12217
12218   if (!(file = fopen(filename, MODE_WRITE)))
12219   {
12220     Warn("cannot write game controller mappings file '%s'", filename);
12221
12222     return;
12223   }
12224
12225   BEGIN_HASH_ITERATION(mappings_hash, itr)
12226   {
12227     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12228   }
12229   END_HASH_ITERATION(mappings_hash, itr)
12230
12231   fclose(file);
12232 }
12233
12234 void SaveSetup_AddGameControllerMapping(char *mapping)
12235 {
12236   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12237   SetupFileHash *mappings_hash = newSetupFileHash();
12238
12239   InitUserDataDirectory();
12240
12241   // load existing personal game controller mappings
12242   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12243
12244   // add new mapping to personal game controller mappings
12245   addGameControllerMappingToHash(mappings_hash, mapping);
12246
12247   // save updated personal game controller mappings
12248   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12249
12250   freeSetupFileHash(mappings_hash);
12251   free(filename);
12252 }
12253
12254 void LoadCustomElementDescriptions(void)
12255 {
12256   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12257   SetupFileHash *setup_file_hash;
12258   int i;
12259
12260   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12261   {
12262     if (element_info[i].custom_description != NULL)
12263     {
12264       free(element_info[i].custom_description);
12265       element_info[i].custom_description = NULL;
12266     }
12267   }
12268
12269   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12270     return;
12271
12272   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12273   {
12274     char *token = getStringCat2(element_info[i].token_name, ".name");
12275     char *value = getHashEntry(setup_file_hash, token);
12276
12277     if (value != NULL)
12278       element_info[i].custom_description = getStringCopy(value);
12279
12280     free(token);
12281   }
12282
12283   freeSetupFileHash(setup_file_hash);
12284 }
12285
12286 static int getElementFromToken(char *token)
12287 {
12288   char *value = getHashEntry(element_token_hash, token);
12289
12290   if (value != NULL)
12291     return atoi(value);
12292
12293   Warn("unknown element token '%s'", token);
12294
12295   return EL_UNDEFINED;
12296 }
12297
12298 void FreeGlobalAnimEventInfo(void)
12299 {
12300   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12301
12302   if (gaei->event_list == NULL)
12303     return;
12304
12305   int i;
12306
12307   for (i = 0; i < gaei->num_event_lists; i++)
12308   {
12309     checked_free(gaei->event_list[i]->event_value);
12310     checked_free(gaei->event_list[i]);
12311   }
12312
12313   checked_free(gaei->event_list);
12314
12315   gaei->event_list = NULL;
12316   gaei->num_event_lists = 0;
12317 }
12318
12319 static int AddGlobalAnimEventList(void)
12320 {
12321   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12322   int list_pos = gaei->num_event_lists++;
12323
12324   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12325                                      sizeof(struct GlobalAnimEventListInfo *));
12326
12327   gaei->event_list[list_pos] =
12328     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12329
12330   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12331
12332   gaeli->event_value = NULL;
12333   gaeli->num_event_values = 0;
12334
12335   return list_pos;
12336 }
12337
12338 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12339 {
12340   // do not add empty global animation events
12341   if (event_value == ANIM_EVENT_NONE)
12342     return list_pos;
12343
12344   // if list position is undefined, create new list
12345   if (list_pos == ANIM_EVENT_UNDEFINED)
12346     list_pos = AddGlobalAnimEventList();
12347
12348   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12349   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12350   int value_pos = gaeli->num_event_values++;
12351
12352   gaeli->event_value = checked_realloc(gaeli->event_value,
12353                                        gaeli->num_event_values * sizeof(int *));
12354
12355   gaeli->event_value[value_pos] = event_value;
12356
12357   return list_pos;
12358 }
12359
12360 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12361 {
12362   if (list_pos == ANIM_EVENT_UNDEFINED)
12363     return ANIM_EVENT_NONE;
12364
12365   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12366   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12367
12368   return gaeli->event_value[value_pos];
12369 }
12370
12371 int GetGlobalAnimEventValueCount(int list_pos)
12372 {
12373   if (list_pos == ANIM_EVENT_UNDEFINED)
12374     return 0;
12375
12376   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12377   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12378
12379   return gaeli->num_event_values;
12380 }
12381
12382 // This function checks if a string <s> of the format "string1, string2, ..."
12383 // exactly contains a string <s_contained>.
12384
12385 static boolean string_has_parameter(char *s, char *s_contained)
12386 {
12387   char *substring;
12388
12389   if (s == NULL || s_contained == NULL)
12390     return FALSE;
12391
12392   if (strlen(s_contained) > strlen(s))
12393     return FALSE;
12394
12395   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12396   {
12397     char next_char = s[strlen(s_contained)];
12398
12399     // check if next character is delimiter or whitespace
12400     if (next_char == ',' || next_char == '\0' ||
12401         next_char == ' ' || next_char == '\t')
12402       return TRUE;
12403   }
12404
12405   // check if string contains another parameter string after a comma
12406   substring = strchr(s, ',');
12407   if (substring == NULL)        // string does not contain a comma
12408     return FALSE;
12409
12410   // advance string pointer to next character after the comma
12411   substring++;
12412
12413   // skip potential whitespaces after the comma
12414   while (*substring == ' ' || *substring == '\t')
12415     substring++;
12416
12417   return string_has_parameter(substring, s_contained);
12418 }
12419
12420 static int get_anim_parameter_value_ce(char *s)
12421 {
12422   char *s_ptr = s;
12423   char *pattern_1 = "ce_change:custom_";
12424   char *pattern_2 = ".page_";
12425   int pattern_1_len = strlen(pattern_1);
12426   char *matching_char = strstr(s_ptr, pattern_1);
12427   int result = ANIM_EVENT_NONE;
12428
12429   if (matching_char == NULL)
12430     return ANIM_EVENT_NONE;
12431
12432   result = ANIM_EVENT_CE_CHANGE;
12433
12434   s_ptr = matching_char + pattern_1_len;
12435
12436   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12437   if (*s_ptr >= '0' && *s_ptr <= '9')
12438   {
12439     int gic_ce_nr = (*s_ptr++ - '0');
12440
12441     if (*s_ptr >= '0' && *s_ptr <= '9')
12442     {
12443       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12444
12445       if (*s_ptr >= '0' && *s_ptr <= '9')
12446         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12447     }
12448
12449     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12450       return ANIM_EVENT_NONE;
12451
12452     // custom element stored as 0 to 255
12453     gic_ce_nr--;
12454
12455     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12456   }
12457   else
12458   {
12459     // invalid custom element number specified
12460
12461     return ANIM_EVENT_NONE;
12462   }
12463
12464   // check for change page number ("page_X" or "page_XX") (optional)
12465   if (strPrefix(s_ptr, pattern_2))
12466   {
12467     s_ptr += strlen(pattern_2);
12468
12469     if (*s_ptr >= '0' && *s_ptr <= '9')
12470     {
12471       int gic_page_nr = (*s_ptr++ - '0');
12472
12473       if (*s_ptr >= '0' && *s_ptr <= '9')
12474         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12475
12476       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12477         return ANIM_EVENT_NONE;
12478
12479       // change page stored as 1 to 32 (0 means "all change pages")
12480
12481       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12482     }
12483     else
12484     {
12485       // invalid animation part number specified
12486
12487       return ANIM_EVENT_NONE;
12488     }
12489   }
12490
12491   // discard result if next character is neither delimiter nor whitespace
12492   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12493         *s_ptr == ' ' || *s_ptr == '\t'))
12494     return ANIM_EVENT_NONE;
12495
12496   return result;
12497 }
12498
12499 static int get_anim_parameter_value(char *s)
12500 {
12501   int event_value[] =
12502   {
12503     ANIM_EVENT_CLICK,
12504     ANIM_EVENT_INIT,
12505     ANIM_EVENT_START,
12506     ANIM_EVENT_END,
12507     ANIM_EVENT_POST
12508   };
12509   char *pattern_1[] =
12510   {
12511     "click:anim_",
12512     "init:anim_",
12513     "start:anim_",
12514     "end:anim_",
12515     "post:anim_"
12516   };
12517   char *pattern_2 = ".part_";
12518   char *matching_char = NULL;
12519   char *s_ptr = s;
12520   int pattern_1_len = 0;
12521   int result = ANIM_EVENT_NONE;
12522   int i;
12523
12524   result = get_anim_parameter_value_ce(s);
12525
12526   if (result != ANIM_EVENT_NONE)
12527     return result;
12528
12529   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12530   {
12531     matching_char = strstr(s_ptr, pattern_1[i]);
12532     pattern_1_len = strlen(pattern_1[i]);
12533     result = event_value[i];
12534
12535     if (matching_char != NULL)
12536       break;
12537   }
12538
12539   if (matching_char == NULL)
12540     return ANIM_EVENT_NONE;
12541
12542   s_ptr = matching_char + pattern_1_len;
12543
12544   // check for main animation number ("anim_X" or "anim_XX")
12545   if (*s_ptr >= '0' && *s_ptr <= '9')
12546   {
12547     int gic_anim_nr = (*s_ptr++ - '0');
12548
12549     if (*s_ptr >= '0' && *s_ptr <= '9')
12550       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12551
12552     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12553       return ANIM_EVENT_NONE;
12554
12555     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12556   }
12557   else
12558   {
12559     // invalid main animation number specified
12560
12561     return ANIM_EVENT_NONE;
12562   }
12563
12564   // check for animation part number ("part_X" or "part_XX") (optional)
12565   if (strPrefix(s_ptr, pattern_2))
12566   {
12567     s_ptr += strlen(pattern_2);
12568
12569     if (*s_ptr >= '0' && *s_ptr <= '9')
12570     {
12571       int gic_part_nr = (*s_ptr++ - '0');
12572
12573       if (*s_ptr >= '0' && *s_ptr <= '9')
12574         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12575
12576       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12577         return ANIM_EVENT_NONE;
12578
12579       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12580     }
12581     else
12582     {
12583       // invalid animation part number specified
12584
12585       return ANIM_EVENT_NONE;
12586     }
12587   }
12588
12589   // discard result if next character is neither delimiter nor whitespace
12590   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12591         *s_ptr == ' ' || *s_ptr == '\t'))
12592     return ANIM_EVENT_NONE;
12593
12594   return result;
12595 }
12596
12597 static int get_anim_parameter_values(char *s)
12598 {
12599   int list_pos = ANIM_EVENT_UNDEFINED;
12600   int event_value = ANIM_EVENT_DEFAULT;
12601
12602   if (string_has_parameter(s, "any"))
12603     event_value |= ANIM_EVENT_ANY;
12604
12605   if (string_has_parameter(s, "click:self") ||
12606       string_has_parameter(s, "click") ||
12607       string_has_parameter(s, "self"))
12608     event_value |= ANIM_EVENT_SELF;
12609
12610   if (string_has_parameter(s, "unclick:any"))
12611     event_value |= ANIM_EVENT_UNCLICK_ANY;
12612
12613   // if animation event found, add it to global animation event list
12614   if (event_value != ANIM_EVENT_NONE)
12615     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12616
12617   while (s != NULL)
12618   {
12619     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12620     event_value = get_anim_parameter_value(s);
12621
12622     // if animation event found, add it to global animation event list
12623     if (event_value != ANIM_EVENT_NONE)
12624       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12625
12626     // continue with next part of the string, starting with next comma
12627     s = strchr(s + 1, ',');
12628   }
12629
12630   return list_pos;
12631 }
12632
12633 static int get_anim_action_parameter_value(char *token)
12634 {
12635   // check most common default case first to massively speed things up
12636   if (strEqual(token, ARG_UNDEFINED))
12637     return ANIM_EVENT_ACTION_NONE;
12638
12639   int result = getImageIDFromToken(token);
12640
12641   if (result == -1)
12642   {
12643     char *gfx_token = getStringCat2("gfx.", token);
12644
12645     result = getImageIDFromToken(gfx_token);
12646
12647     checked_free(gfx_token);
12648   }
12649
12650   if (result == -1)
12651   {
12652     Key key = getKeyFromX11KeyName(token);
12653
12654     if (key != KSYM_UNDEFINED)
12655       result = -(int)key;
12656   }
12657
12658   if (result == -1)
12659   {
12660     if (isURL(token))
12661     {
12662       result = get_hash_from_string(token);     // unsigned int => int
12663       result = ABS(result);                     // may be negative now
12664       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12665
12666       setHashEntry(anim_url_hash, int2str(result, 0), token);
12667     }
12668   }
12669
12670   if (result == -1)
12671     result = ANIM_EVENT_ACTION_NONE;
12672
12673   return result;
12674 }
12675
12676 int get_parameter_value(char *value_raw, char *suffix, int type)
12677 {
12678   char *value = getStringToLower(value_raw);
12679   int result = 0;       // probably a save default value
12680
12681   if (strEqual(suffix, ".direction"))
12682   {
12683     result = (strEqual(value, "left")  ? MV_LEFT :
12684               strEqual(value, "right") ? MV_RIGHT :
12685               strEqual(value, "up")    ? MV_UP :
12686               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12687   }
12688   else if (strEqual(suffix, ".position"))
12689   {
12690     result = (strEqual(value, "left")   ? POS_LEFT :
12691               strEqual(value, "right")  ? POS_RIGHT :
12692               strEqual(value, "top")    ? POS_TOP :
12693               strEqual(value, "upper")  ? POS_UPPER :
12694               strEqual(value, "middle") ? POS_MIDDLE :
12695               strEqual(value, "lower")  ? POS_LOWER :
12696               strEqual(value, "bottom") ? POS_BOTTOM :
12697               strEqual(value, "any")    ? POS_ANY :
12698               strEqual(value, "ce")     ? POS_CE :
12699               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12700               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12701   }
12702   else if (strEqual(suffix, ".align"))
12703   {
12704     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12705               strEqual(value, "right")  ? ALIGN_RIGHT :
12706               strEqual(value, "center") ? ALIGN_CENTER :
12707               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12708   }
12709   else if (strEqual(suffix, ".valign"))
12710   {
12711     result = (strEqual(value, "top")    ? VALIGN_TOP :
12712               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12713               strEqual(value, "middle") ? VALIGN_MIDDLE :
12714               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12715   }
12716   else if (strEqual(suffix, ".anim_mode"))
12717   {
12718     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12719               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12720               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12721               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12722               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12723               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12724               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12725               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12726               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12727               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12728               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12729               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12730               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12731               string_has_parameter(value, "all")        ? ANIM_ALL :
12732               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12733               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12734               ANIM_DEFAULT);
12735
12736     if (string_has_parameter(value, "once"))
12737       result |= ANIM_ONCE;
12738
12739     if (string_has_parameter(value, "reverse"))
12740       result |= ANIM_REVERSE;
12741
12742     if (string_has_parameter(value, "opaque_player"))
12743       result |= ANIM_OPAQUE_PLAYER;
12744
12745     if (string_has_parameter(value, "static_panel"))
12746       result |= ANIM_STATIC_PANEL;
12747   }
12748   else if (strEqual(suffix, ".init_event") ||
12749            strEqual(suffix, ".anim_event"))
12750   {
12751     result = get_anim_parameter_values(value);
12752   }
12753   else if (strEqual(suffix, ".init_delay_action") ||
12754            strEqual(suffix, ".anim_delay_action") ||
12755            strEqual(suffix, ".post_delay_action") ||
12756            strEqual(suffix, ".init_event_action") ||
12757            strEqual(suffix, ".anim_event_action"))
12758   {
12759     result = get_anim_action_parameter_value(value_raw);
12760   }
12761   else if (strEqual(suffix, ".class"))
12762   {
12763     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12764               get_hash_from_string(value));
12765   }
12766   else if (strEqual(suffix, ".style"))
12767   {
12768     result = STYLE_DEFAULT;
12769
12770     if (string_has_parameter(value, "accurate_borders"))
12771       result |= STYLE_ACCURATE_BORDERS;
12772
12773     if (string_has_parameter(value, "inner_corners"))
12774       result |= STYLE_INNER_CORNERS;
12775
12776     if (string_has_parameter(value, "reverse"))
12777       result |= STYLE_REVERSE;
12778
12779     if (string_has_parameter(value, "leftmost_position"))
12780       result |= STYLE_LEFTMOST_POSITION;
12781
12782     if (string_has_parameter(value, "block_clicks"))
12783       result |= STYLE_BLOCK;
12784
12785     if (string_has_parameter(value, "passthrough_clicks"))
12786       result |= STYLE_PASSTHROUGH;
12787
12788     if (string_has_parameter(value, "multiple_actions"))
12789       result |= STYLE_MULTIPLE_ACTIONS;
12790
12791     if (string_has_parameter(value, "consume_ce_event"))
12792       result |= STYLE_CONSUME_CE_EVENT;
12793   }
12794   else if (strEqual(suffix, ".fade_mode"))
12795   {
12796     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12797               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12798               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12799               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12800               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12801               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12802               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12803               FADE_MODE_DEFAULT);
12804   }
12805   else if (strEqual(suffix, ".auto_delay_unit"))
12806   {
12807     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12808               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12809               AUTO_DELAY_UNIT_DEFAULT);
12810   }
12811   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12812   {
12813     result = gfx.get_font_from_token_function(value);
12814   }
12815   else          // generic parameter of type integer or boolean
12816   {
12817     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12818               type == TYPE_INTEGER ? get_integer_from_string(value) :
12819               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12820               ARG_UNDEFINED_VALUE);
12821   }
12822
12823   free(value);
12824
12825   return result;
12826 }
12827
12828 static int get_token_parameter_value(char *token, char *value_raw)
12829 {
12830   char *suffix;
12831
12832   if (token == NULL || value_raw == NULL)
12833     return ARG_UNDEFINED_VALUE;
12834
12835   suffix = strrchr(token, '.');
12836   if (suffix == NULL)
12837     suffix = token;
12838
12839   if (strEqual(suffix, ".element"))
12840     return getElementFromToken(value_raw);
12841
12842   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12843   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12844 }
12845
12846 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12847                                      boolean ignore_defaults)
12848 {
12849   int i;
12850
12851   for (i = 0; image_config_vars[i].token != NULL; i++)
12852   {
12853     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12854
12855     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12856     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12857       continue;
12858
12859     if (value != NULL)
12860       *image_config_vars[i].value =
12861         get_token_parameter_value(image_config_vars[i].token, value);
12862   }
12863 }
12864
12865 void InitMenuDesignSettings_Static(void)
12866 {
12867   // always start with reliable default values from static default config
12868   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12869 }
12870
12871 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12872 {
12873   int i;
12874
12875   // the following initializes hierarchical values from static configuration
12876
12877   // special case: initialize "ARG_DEFAULT" values in static default config
12878   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12879   titlescreen_initial_first_default.fade_mode  =
12880     title_initial_first_default.fade_mode;
12881   titlescreen_initial_first_default.fade_delay =
12882     title_initial_first_default.fade_delay;
12883   titlescreen_initial_first_default.post_delay =
12884     title_initial_first_default.post_delay;
12885   titlescreen_initial_first_default.auto_delay =
12886     title_initial_first_default.auto_delay;
12887   titlescreen_initial_first_default.auto_delay_unit =
12888     title_initial_first_default.auto_delay_unit;
12889   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12890   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12891   titlescreen_first_default.post_delay = title_first_default.post_delay;
12892   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12893   titlescreen_first_default.auto_delay_unit =
12894     title_first_default.auto_delay_unit;
12895   titlemessage_initial_first_default.fade_mode  =
12896     title_initial_first_default.fade_mode;
12897   titlemessage_initial_first_default.fade_delay =
12898     title_initial_first_default.fade_delay;
12899   titlemessage_initial_first_default.post_delay =
12900     title_initial_first_default.post_delay;
12901   titlemessage_initial_first_default.auto_delay =
12902     title_initial_first_default.auto_delay;
12903   titlemessage_initial_first_default.auto_delay_unit =
12904     title_initial_first_default.auto_delay_unit;
12905   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12906   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12907   titlemessage_first_default.post_delay = title_first_default.post_delay;
12908   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12909   titlemessage_first_default.auto_delay_unit =
12910     title_first_default.auto_delay_unit;
12911
12912   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12913   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12914   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12915   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12916   titlescreen_initial_default.auto_delay_unit =
12917     title_initial_default.auto_delay_unit;
12918   titlescreen_default.fade_mode  = title_default.fade_mode;
12919   titlescreen_default.fade_delay = title_default.fade_delay;
12920   titlescreen_default.post_delay = title_default.post_delay;
12921   titlescreen_default.auto_delay = title_default.auto_delay;
12922   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12923   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12924   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12925   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12926   titlemessage_initial_default.auto_delay_unit =
12927     title_initial_default.auto_delay_unit;
12928   titlemessage_default.fade_mode  = title_default.fade_mode;
12929   titlemessage_default.fade_delay = title_default.fade_delay;
12930   titlemessage_default.post_delay = title_default.post_delay;
12931   titlemessage_default.auto_delay = title_default.auto_delay;
12932   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12933
12934   // special case: initialize "ARG_DEFAULT" values in static default config
12935   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12936   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12937   {
12938     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12939     titlescreen_first[i] = titlescreen_first_default;
12940     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12941     titlemessage_first[i] = titlemessage_first_default;
12942
12943     titlescreen_initial[i] = titlescreen_initial_default;
12944     titlescreen[i] = titlescreen_default;
12945     titlemessage_initial[i] = titlemessage_initial_default;
12946     titlemessage[i] = titlemessage_default;
12947   }
12948
12949   // special case: initialize "ARG_DEFAULT" values in static default config
12950   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12951   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12952   {
12953     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12954       continue;
12955
12956     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12957     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12958     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12959   }
12960
12961   // special case: initialize "ARG_DEFAULT" values in static default config
12962   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12963   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12964   {
12965     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12966     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12967     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12968
12969     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12970       continue;
12971
12972     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12973   }
12974 }
12975
12976 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12977 {
12978   static struct
12979   {
12980     struct XY *dst, *src;
12981   }
12982   game_buttons_xy[] =
12983   {
12984     { &game.button.save,        &game.button.stop       },
12985     { &game.button.pause2,      &game.button.pause      },
12986     { &game.button.load,        &game.button.play       },
12987     { &game.button.undo,        &game.button.stop       },
12988     { &game.button.redo,        &game.button.play       },
12989
12990     { NULL,                     NULL                    }
12991   };
12992   int i, j;
12993
12994   // special case: initialize later added SETUP list size from LEVELS value
12995   if (menu.list_size[GAME_MODE_SETUP] == -1)
12996     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12997
12998   // set default position for snapshot buttons to stop/pause/play buttons
12999   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13000     if ((*game_buttons_xy[i].dst).x == -1 &&
13001         (*game_buttons_xy[i].dst).y == -1)
13002       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13003
13004   // --------------------------------------------------------------------------
13005   // dynamic viewports (including playfield margins, borders and alignments)
13006   // --------------------------------------------------------------------------
13007
13008   // dynamic viewports currently only supported for landscape mode
13009   int display_width  = MAX(video.display_width, video.display_height);
13010   int display_height = MIN(video.display_width, video.display_height);
13011
13012   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13013   {
13014     struct RectWithBorder *vp_window    = &viewport.window[i];
13015     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13016     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13017     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13018     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13019     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13020     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13021     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13022
13023     // adjust window size if min/max width/height is specified
13024
13025     if (vp_window->min_width != -1)
13026     {
13027       int window_width = display_width;
13028
13029       // when using static window height, use aspect ratio of display
13030       if (vp_window->min_height == -1)
13031         window_width = vp_window->height * display_width / display_height;
13032
13033       vp_window->width = MAX(vp_window->min_width, window_width);
13034     }
13035
13036     if (vp_window->min_height != -1)
13037     {
13038       int window_height = display_height;
13039
13040       // when using static window width, use aspect ratio of display
13041       if (vp_window->min_width == -1)
13042         window_height = vp_window->width * display_height / display_width;
13043
13044       vp_window->height = MAX(vp_window->min_height, window_height);
13045     }
13046
13047     if (vp_window->max_width != -1)
13048       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13049
13050     if (vp_window->max_height != -1)
13051       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13052
13053     int playfield_width  = vp_window->width;
13054     int playfield_height = vp_window->height;
13055
13056     // adjust playfield size and position according to specified margins
13057
13058     playfield_width  -= vp_playfield->margin_left;
13059     playfield_width  -= vp_playfield->margin_right;
13060
13061     playfield_height -= vp_playfield->margin_top;
13062     playfield_height -= vp_playfield->margin_bottom;
13063
13064     // adjust playfield size if min/max width/height is specified
13065
13066     if (vp_playfield->min_width != -1)
13067       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13068
13069     if (vp_playfield->min_height != -1)
13070       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13071
13072     if (vp_playfield->max_width != -1)
13073       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13074
13075     if (vp_playfield->max_height != -1)
13076       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13077
13078     // adjust playfield position according to specified alignment
13079
13080     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13081       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13082     else if (vp_playfield->align == ALIGN_CENTER)
13083       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13084     else if (vp_playfield->align == ALIGN_RIGHT)
13085       vp_playfield->x += playfield_width - vp_playfield->width;
13086
13087     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13088       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13089     else if (vp_playfield->valign == VALIGN_MIDDLE)
13090       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13091     else if (vp_playfield->valign == VALIGN_BOTTOM)
13092       vp_playfield->y += playfield_height - vp_playfield->height;
13093
13094     vp_playfield->x += vp_playfield->margin_left;
13095     vp_playfield->y += vp_playfield->margin_top;
13096
13097     // adjust individual playfield borders if only default border is specified
13098
13099     if (vp_playfield->border_left == -1)
13100       vp_playfield->border_left = vp_playfield->border_size;
13101     if (vp_playfield->border_right == -1)
13102       vp_playfield->border_right = vp_playfield->border_size;
13103     if (vp_playfield->border_top == -1)
13104       vp_playfield->border_top = vp_playfield->border_size;
13105     if (vp_playfield->border_bottom == -1)
13106       vp_playfield->border_bottom = vp_playfield->border_size;
13107
13108     // set dynamic playfield borders if borders are specified as undefined
13109     // (but only if window size was dynamic and playfield size was static)
13110
13111     if (dynamic_window_width && !dynamic_playfield_width)
13112     {
13113       if (vp_playfield->border_left == -1)
13114       {
13115         vp_playfield->border_left = (vp_playfield->x -
13116                                      vp_playfield->margin_left);
13117         vp_playfield->x     -= vp_playfield->border_left;
13118         vp_playfield->width += vp_playfield->border_left;
13119       }
13120
13121       if (vp_playfield->border_right == -1)
13122       {
13123         vp_playfield->border_right = (vp_window->width -
13124                                       vp_playfield->x -
13125                                       vp_playfield->width -
13126                                       vp_playfield->margin_right);
13127         vp_playfield->width += vp_playfield->border_right;
13128       }
13129     }
13130
13131     if (dynamic_window_height && !dynamic_playfield_height)
13132     {
13133       if (vp_playfield->border_top == -1)
13134       {
13135         vp_playfield->border_top = (vp_playfield->y -
13136                                     vp_playfield->margin_top);
13137         vp_playfield->y      -= vp_playfield->border_top;
13138         vp_playfield->height += vp_playfield->border_top;
13139       }
13140
13141       if (vp_playfield->border_bottom == -1)
13142       {
13143         vp_playfield->border_bottom = (vp_window->height -
13144                                        vp_playfield->y -
13145                                        vp_playfield->height -
13146                                        vp_playfield->margin_bottom);
13147         vp_playfield->height += vp_playfield->border_bottom;
13148       }
13149     }
13150
13151     // adjust playfield size to be a multiple of a defined alignment tile size
13152
13153     int align_size = vp_playfield->align_size;
13154     int playfield_xtiles = vp_playfield->width  / align_size;
13155     int playfield_ytiles = vp_playfield->height / align_size;
13156     int playfield_width_corrected  = playfield_xtiles * align_size;
13157     int playfield_height_corrected = playfield_ytiles * align_size;
13158     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13159                                  i == GFX_SPECIAL_ARG_EDITOR);
13160
13161     if (is_playfield_mode &&
13162         dynamic_playfield_width &&
13163         vp_playfield->width != playfield_width_corrected)
13164     {
13165       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13166
13167       vp_playfield->width = playfield_width_corrected;
13168
13169       if (vp_playfield->align == ALIGN_LEFT)
13170       {
13171         vp_playfield->border_left += playfield_xdiff;
13172       }
13173       else if (vp_playfield->align == ALIGN_RIGHT)
13174       {
13175         vp_playfield->border_right += playfield_xdiff;
13176       }
13177       else if (vp_playfield->align == ALIGN_CENTER)
13178       {
13179         int border_left_diff  = playfield_xdiff / 2;
13180         int border_right_diff = playfield_xdiff - border_left_diff;
13181
13182         vp_playfield->border_left  += border_left_diff;
13183         vp_playfield->border_right += border_right_diff;
13184       }
13185     }
13186
13187     if (is_playfield_mode &&
13188         dynamic_playfield_height &&
13189         vp_playfield->height != playfield_height_corrected)
13190     {
13191       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13192
13193       vp_playfield->height = playfield_height_corrected;
13194
13195       if (vp_playfield->valign == VALIGN_TOP)
13196       {
13197         vp_playfield->border_top += playfield_ydiff;
13198       }
13199       else if (vp_playfield->align == VALIGN_BOTTOM)
13200       {
13201         vp_playfield->border_right += playfield_ydiff;
13202       }
13203       else if (vp_playfield->align == VALIGN_MIDDLE)
13204       {
13205         int border_top_diff    = playfield_ydiff / 2;
13206         int border_bottom_diff = playfield_ydiff - border_top_diff;
13207
13208         vp_playfield->border_top    += border_top_diff;
13209         vp_playfield->border_bottom += border_bottom_diff;
13210       }
13211     }
13212
13213     // adjust door positions according to specified alignment
13214
13215     for (j = 0; j < 2; j++)
13216     {
13217       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13218
13219       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13220         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13221       else if (vp_door->align == ALIGN_CENTER)
13222         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13223       else if (vp_door->align == ALIGN_RIGHT)
13224         vp_door->x += vp_window->width - vp_door->width;
13225
13226       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13227         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13228       else if (vp_door->valign == VALIGN_MIDDLE)
13229         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13230       else if (vp_door->valign == VALIGN_BOTTOM)
13231         vp_door->y += vp_window->height - vp_door->height;
13232     }
13233   }
13234 }
13235
13236 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13237 {
13238   static struct
13239   {
13240     struct XYTileSize *dst, *src;
13241     int graphic;
13242   }
13243   editor_buttons_xy[] =
13244   {
13245     {
13246       &editor.button.element_left,      &editor.palette.element_left,
13247       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13248     },
13249     {
13250       &editor.button.element_middle,    &editor.palette.element_middle,
13251       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13252     },
13253     {
13254       &editor.button.element_right,     &editor.palette.element_right,
13255       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13256     },
13257
13258     { NULL,                     NULL                    }
13259   };
13260   int i;
13261
13262   // set default position for element buttons to element graphics
13263   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13264   {
13265     if ((*editor_buttons_xy[i].dst).x == -1 &&
13266         (*editor_buttons_xy[i].dst).y == -1)
13267     {
13268       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13269
13270       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13271
13272       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13273     }
13274   }
13275
13276   // adjust editor palette rows and columns if specified to be dynamic
13277
13278   if (editor.palette.cols == -1)
13279   {
13280     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13281     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13282     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13283
13284     editor.palette.cols = (vp_width - sc_width) / bt_width;
13285
13286     if (editor.palette.x == -1)
13287     {
13288       int palette_width = editor.palette.cols * bt_width + sc_width;
13289
13290       editor.palette.x = (vp_width - palette_width) / 2;
13291     }
13292   }
13293
13294   if (editor.palette.rows == -1)
13295   {
13296     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13297     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13298     int tx_height = getFontHeight(FONT_TEXT_2);
13299
13300     editor.palette.rows = (vp_height - tx_height) / bt_height;
13301
13302     if (editor.palette.y == -1)
13303     {
13304       int palette_height = editor.palette.rows * bt_height + tx_height;
13305
13306       editor.palette.y = (vp_height - palette_height) / 2;
13307     }
13308   }
13309 }
13310
13311 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13312                                                       boolean initialize)
13313 {
13314   // special case: check if network and preview player positions are redefined,
13315   // to compare this later against the main menu level preview being redefined
13316   struct TokenIntPtrInfo menu_config_players[] =
13317   {
13318     { "main.network_players.x", &menu.main.network_players.redefined    },
13319     { "main.network_players.y", &menu.main.network_players.redefined    },
13320     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13321     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13322     { "preview.x",              &preview.redefined                      },
13323     { "preview.y",              &preview.redefined                      }
13324   };
13325   int i;
13326
13327   if (initialize)
13328   {
13329     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13330       *menu_config_players[i].value = FALSE;
13331   }
13332   else
13333   {
13334     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13335       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13336         *menu_config_players[i].value = TRUE;
13337   }
13338 }
13339
13340 static void InitMenuDesignSettings_PreviewPlayers(void)
13341 {
13342   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13343 }
13344
13345 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13346 {
13347   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13348 }
13349
13350 static void LoadMenuDesignSettingsFromFilename(char *filename)
13351 {
13352   static struct TitleFadingInfo tfi;
13353   static struct TitleMessageInfo tmi;
13354   static struct TokenInfo title_tokens[] =
13355   {
13356     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13357     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13358     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13359     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13360     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13361
13362     { -1,               NULL,                   NULL                    }
13363   };
13364   static struct TokenInfo titlemessage_tokens[] =
13365   {
13366     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13367     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13368     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13369     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13370     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13371     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13372     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13373     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13374     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13375     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13376     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13377     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13378     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13379     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13380     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13381     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13382     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13383     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13384
13385     { -1,               NULL,                   NULL                    }
13386   };
13387   static struct
13388   {
13389     struct TitleFadingInfo *info;
13390     char *text;
13391   }
13392   title_info[] =
13393   {
13394     // initialize first titles from "enter screen" definitions, if defined
13395     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13396     { &title_first_default,             "menu.enter_screen.TITLE"       },
13397
13398     // initialize title screens from "next screen" definitions, if defined
13399     { &title_initial_default,           "menu.next_screen.TITLE"        },
13400     { &title_default,                   "menu.next_screen.TITLE"        },
13401
13402     { NULL,                             NULL                            }
13403   };
13404   static struct
13405   {
13406     struct TitleMessageInfo *array;
13407     char *text;
13408   }
13409   titlemessage_arrays[] =
13410   {
13411     // initialize first titles from "enter screen" definitions, if defined
13412     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13413     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13414     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13415     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13416
13417     // initialize titles from "next screen" definitions, if defined
13418     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13419     { titlescreen,                      "menu.next_screen.TITLE"        },
13420     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13421     { titlemessage,                     "menu.next_screen.TITLE"        },
13422
13423     // overwrite titles with title definitions, if defined
13424     { titlescreen_initial_first,        "[title_initial]"               },
13425     { titlescreen_first,                "[title]"                       },
13426     { titlemessage_initial_first,       "[title_initial]"               },
13427     { titlemessage_first,               "[title]"                       },
13428
13429     { titlescreen_initial,              "[title_initial]"               },
13430     { titlescreen,                      "[title]"                       },
13431     { titlemessage_initial,             "[title_initial]"               },
13432     { titlemessage,                     "[title]"                       },
13433
13434     // overwrite titles with title screen/message definitions, if defined
13435     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13436     { titlescreen_first,                "[titlescreen]"                 },
13437     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13438     { titlemessage_first,               "[titlemessage]"                },
13439
13440     { titlescreen_initial,              "[titlescreen_initial]"         },
13441     { titlescreen,                      "[titlescreen]"                 },
13442     { titlemessage_initial,             "[titlemessage_initial]"        },
13443     { titlemessage,                     "[titlemessage]"                },
13444
13445     { NULL,                             NULL                            }
13446   };
13447   SetupFileHash *setup_file_hash;
13448   int i, j, k;
13449
13450   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13451     return;
13452
13453   // the following initializes hierarchical values from dynamic configuration
13454
13455   // special case: initialize with default values that may be overwritten
13456   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13457   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13458   {
13459     struct TokenIntPtrInfo menu_config[] =
13460     {
13461       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13462       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13463       { "menu.list_size",       &menu.list_size[i]      }
13464     };
13465
13466     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13467     {
13468       char *token = menu_config[j].token;
13469       char *value = getHashEntry(setup_file_hash, token);
13470
13471       if (value != NULL)
13472         *menu_config[j].value = get_integer_from_string(value);
13473     }
13474   }
13475
13476   // special case: initialize with default values that may be overwritten
13477   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13478   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13479   {
13480     struct TokenIntPtrInfo menu_config[] =
13481     {
13482       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13483       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13484       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13485       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13486       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13487     };
13488
13489     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13490     {
13491       char *token = menu_config[j].token;
13492       char *value = getHashEntry(setup_file_hash, token);
13493
13494       if (value != NULL)
13495         *menu_config[j].value = get_integer_from_string(value);
13496     }
13497   }
13498
13499   // special case: initialize with default values that may be overwritten
13500   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13501   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13502   {
13503     struct TokenIntPtrInfo menu_config[] =
13504     {
13505       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13506       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13507     };
13508
13509     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13510     {
13511       char *token = menu_config[j].token;
13512       char *value = getHashEntry(setup_file_hash, token);
13513
13514       if (value != NULL)
13515         *menu_config[j].value = get_integer_from_string(value);
13516     }
13517   }
13518
13519   // special case: initialize with default values that may be overwritten
13520   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13521   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13522   {
13523     struct TokenIntPtrInfo menu_config[] =
13524     {
13525       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13526       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13527       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13528       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13529       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13530       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13531       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13532       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13533       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13534       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13535     };
13536
13537     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13538     {
13539       char *token = menu_config[j].token;
13540       char *value = getHashEntry(setup_file_hash, token);
13541
13542       if (value != NULL)
13543         *menu_config[j].value = get_integer_from_string(value);
13544     }
13545   }
13546
13547   // special case: initialize with default values that may be overwritten
13548   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13549   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13550   {
13551     struct TokenIntPtrInfo menu_config[] =
13552     {
13553       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13554       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13555       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13556       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13557       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13558       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13559       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13560       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13561       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13562     };
13563
13564     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13565     {
13566       char *token = menu_config[j].token;
13567       char *value = getHashEntry(setup_file_hash, token);
13568
13569       if (value != NULL)
13570         *menu_config[j].value = get_token_parameter_value(token, value);
13571     }
13572   }
13573
13574   // special case: initialize with default values that may be overwritten
13575   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13576   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13577   {
13578     struct
13579     {
13580       char *token_prefix;
13581       struct RectWithBorder *struct_ptr;
13582     }
13583     vp_struct[] =
13584     {
13585       { "viewport.window",      &viewport.window[i]     },
13586       { "viewport.playfield",   &viewport.playfield[i]  },
13587       { "viewport.door_1",      &viewport.door_1[i]     },
13588       { "viewport.door_2",      &viewport.door_2[i]     }
13589     };
13590
13591     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13592     {
13593       struct TokenIntPtrInfo vp_config[] =
13594       {
13595         { ".x",                 &vp_struct[j].struct_ptr->x             },
13596         { ".y",                 &vp_struct[j].struct_ptr->y             },
13597         { ".width",             &vp_struct[j].struct_ptr->width         },
13598         { ".height",            &vp_struct[j].struct_ptr->height        },
13599         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13600         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13601         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13602         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13603         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13604         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13605         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13606         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13607         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13608         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13609         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13610         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13611         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13612         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13613         { ".align",             &vp_struct[j].struct_ptr->align         },
13614         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13615       };
13616
13617       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13618       {
13619         char *token = getStringCat2(vp_struct[j].token_prefix,
13620                                     vp_config[k].token);
13621         char *value = getHashEntry(setup_file_hash, token);
13622
13623         if (value != NULL)
13624           *vp_config[k].value = get_token_parameter_value(token, value);
13625
13626         free(token);
13627       }
13628     }
13629   }
13630
13631   // special case: initialize with default values that may be overwritten
13632   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13633   for (i = 0; title_info[i].info != NULL; i++)
13634   {
13635     struct TitleFadingInfo *info = title_info[i].info;
13636     char *base_token = title_info[i].text;
13637
13638     for (j = 0; title_tokens[j].type != -1; j++)
13639     {
13640       char *token = getStringCat2(base_token, title_tokens[j].text);
13641       char *value = getHashEntry(setup_file_hash, token);
13642
13643       if (value != NULL)
13644       {
13645         int parameter_value = get_token_parameter_value(token, value);
13646
13647         tfi = *info;
13648
13649         *(int *)title_tokens[j].value = (int)parameter_value;
13650
13651         *info = tfi;
13652       }
13653
13654       free(token);
13655     }
13656   }
13657
13658   // special case: initialize with default values that may be overwritten
13659   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13660   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13661   {
13662     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13663     char *base_token = titlemessage_arrays[i].text;
13664
13665     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13666     {
13667       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13668       char *value = getHashEntry(setup_file_hash, token);
13669
13670       if (value != NULL)
13671       {
13672         int parameter_value = get_token_parameter_value(token, value);
13673
13674         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13675         {
13676           tmi = array[k];
13677
13678           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13679             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13680           else
13681             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13682
13683           array[k] = tmi;
13684         }
13685       }
13686
13687       free(token);
13688     }
13689   }
13690
13691   // read (and overwrite with) values that may be specified in config file
13692   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13693
13694   // special case: check if network and preview player positions are redefined
13695   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13696
13697   freeSetupFileHash(setup_file_hash);
13698 }
13699
13700 void LoadMenuDesignSettings(void)
13701 {
13702   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13703
13704   InitMenuDesignSettings_Static();
13705   InitMenuDesignSettings_SpecialPreProcessing();
13706   InitMenuDesignSettings_PreviewPlayers();
13707
13708   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13709   {
13710     // first look for special settings configured in level series config
13711     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13712
13713     if (fileExists(filename_base))
13714       LoadMenuDesignSettingsFromFilename(filename_base);
13715   }
13716
13717   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13718
13719   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13720     LoadMenuDesignSettingsFromFilename(filename_local);
13721
13722   InitMenuDesignSettings_SpecialPostProcessing();
13723 }
13724
13725 void LoadMenuDesignSettings_AfterGraphics(void)
13726 {
13727   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13728 }
13729
13730 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13731                                 boolean ignore_defaults)
13732 {
13733   int i;
13734
13735   for (i = 0; sound_config_vars[i].token != NULL; i++)
13736   {
13737     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13738
13739     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13740     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13741       continue;
13742
13743     if (value != NULL)
13744       *sound_config_vars[i].value =
13745         get_token_parameter_value(sound_config_vars[i].token, value);
13746   }
13747 }
13748
13749 void InitSoundSettings_Static(void)
13750 {
13751   // always start with reliable default values from static default config
13752   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13753 }
13754
13755 static void LoadSoundSettingsFromFilename(char *filename)
13756 {
13757   SetupFileHash *setup_file_hash;
13758
13759   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13760     return;
13761
13762   // read (and overwrite with) values that may be specified in config file
13763   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13764
13765   freeSetupFileHash(setup_file_hash);
13766 }
13767
13768 void LoadSoundSettings(void)
13769 {
13770   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13771
13772   InitSoundSettings_Static();
13773
13774   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13775   {
13776     // first look for special settings configured in level series config
13777     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13778
13779     if (fileExists(filename_base))
13780       LoadSoundSettingsFromFilename(filename_base);
13781   }
13782
13783   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13784
13785   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13786     LoadSoundSettingsFromFilename(filename_local);
13787 }
13788
13789 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13790 {
13791   char *filename = getEditorSetupFilename();
13792   SetupFileList *setup_file_list, *list;
13793   SetupFileHash *element_hash;
13794   int num_unknown_tokens = 0;
13795   int i;
13796
13797   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13798     return;
13799
13800   element_hash = newSetupFileHash();
13801
13802   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13803     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13804
13805   // determined size may be larger than needed (due to unknown elements)
13806   *num_elements = 0;
13807   for (list = setup_file_list; list != NULL; list = list->next)
13808     (*num_elements)++;
13809
13810   // add space for up to 3 more elements for padding that may be needed
13811   *num_elements += 3;
13812
13813   // free memory for old list of elements, if needed
13814   checked_free(*elements);
13815
13816   // allocate memory for new list of elements
13817   *elements = checked_malloc(*num_elements * sizeof(int));
13818
13819   *num_elements = 0;
13820   for (list = setup_file_list; list != NULL; list = list->next)
13821   {
13822     char *value = getHashEntry(element_hash, list->token);
13823
13824     if (value == NULL)          // try to find obsolete token mapping
13825     {
13826       char *mapped_token = get_mapped_token(list->token);
13827
13828       if (mapped_token != NULL)
13829       {
13830         value = getHashEntry(element_hash, mapped_token);
13831
13832         free(mapped_token);
13833       }
13834     }
13835
13836     if (value != NULL)
13837     {
13838       (*elements)[(*num_elements)++] = atoi(value);
13839     }
13840     else
13841     {
13842       if (num_unknown_tokens == 0)
13843       {
13844         Warn("---");
13845         Warn("unknown token(s) found in config file:");
13846         Warn("- config file: '%s'", filename);
13847
13848         num_unknown_tokens++;
13849       }
13850
13851       Warn("- token: '%s'", list->token);
13852     }
13853   }
13854
13855   if (num_unknown_tokens > 0)
13856     Warn("---");
13857
13858   while (*num_elements % 4)     // pad with empty elements, if needed
13859     (*elements)[(*num_elements)++] = EL_EMPTY;
13860
13861   freeSetupFileList(setup_file_list);
13862   freeSetupFileHash(element_hash);
13863
13864 #if 0
13865   for (i = 0; i < *num_elements; i++)
13866     Debug("editor", "element '%s' [%d]\n",
13867           element_info[(*elements)[i]].token_name, (*elements)[i]);
13868 #endif
13869 }
13870
13871 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13872                                                      boolean is_sound)
13873 {
13874   SetupFileHash *setup_file_hash = NULL;
13875   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13876   char *filename_music, *filename_prefix, *filename_info;
13877   struct
13878   {
13879     char *token;
13880     char **value_ptr;
13881   }
13882   token_to_value_ptr[] =
13883   {
13884     { "title_header",   &tmp_music_file_info.title_header       },
13885     { "artist_header",  &tmp_music_file_info.artist_header      },
13886     { "album_header",   &tmp_music_file_info.album_header       },
13887     { "year_header",    &tmp_music_file_info.year_header        },
13888     { "played_header",  &tmp_music_file_info.played_header      },
13889
13890     { "title",          &tmp_music_file_info.title              },
13891     { "artist",         &tmp_music_file_info.artist             },
13892     { "album",          &tmp_music_file_info.album              },
13893     { "year",           &tmp_music_file_info.year               },
13894     { "played",         &tmp_music_file_info.played             },
13895
13896     { NULL,             NULL                                    },
13897   };
13898   int i;
13899
13900   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13901                     getCustomMusicFilename(basename));
13902
13903   if (filename_music == NULL)
13904     return NULL;
13905
13906   // ---------- try to replace file extension ----------
13907
13908   filename_prefix = getStringCopy(filename_music);
13909   if (strrchr(filename_prefix, '.') != NULL)
13910     *strrchr(filename_prefix, '.') = '\0';
13911   filename_info = getStringCat2(filename_prefix, ".txt");
13912
13913   if (fileExists(filename_info))
13914     setup_file_hash = loadSetupFileHash(filename_info);
13915
13916   free(filename_prefix);
13917   free(filename_info);
13918
13919   if (setup_file_hash == NULL)
13920   {
13921     // ---------- try to add file extension ----------
13922
13923     filename_prefix = getStringCopy(filename_music);
13924     filename_info = getStringCat2(filename_prefix, ".txt");
13925
13926     if (fileExists(filename_info))
13927       setup_file_hash = loadSetupFileHash(filename_info);
13928
13929     free(filename_prefix);
13930     free(filename_info);
13931   }
13932
13933   if (setup_file_hash == NULL)
13934     return NULL;
13935
13936   // ---------- music file info found ----------
13937
13938   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13939
13940   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13941   {
13942     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13943
13944     *token_to_value_ptr[i].value_ptr =
13945       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13946   }
13947
13948   tmp_music_file_info.basename = getStringCopy(basename);
13949   tmp_music_file_info.music = music;
13950   tmp_music_file_info.is_sound = is_sound;
13951
13952   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13953   *new_music_file_info = tmp_music_file_info;
13954
13955   return new_music_file_info;
13956 }
13957
13958 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13959 {
13960   return get_music_file_info_ext(basename, music, FALSE);
13961 }
13962
13963 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13964 {
13965   return get_music_file_info_ext(basename, sound, TRUE);
13966 }
13967
13968 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13969                                      char *basename, boolean is_sound)
13970 {
13971   for (; list != NULL; list = list->next)
13972     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13973       return TRUE;
13974
13975   return FALSE;
13976 }
13977
13978 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13979 {
13980   return music_info_listed_ext(list, basename, FALSE);
13981 }
13982
13983 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13984 {
13985   return music_info_listed_ext(list, basename, TRUE);
13986 }
13987
13988 void LoadMusicInfo(void)
13989 {
13990   int num_music_noconf = getMusicListSize_NoConf();
13991   int num_music = getMusicListSize();
13992   int num_sounds = getSoundListSize();
13993   struct FileInfo *music, *sound;
13994   struct MusicFileInfo *next, **new;
13995
13996   int i;
13997
13998   while (music_file_info != NULL)
13999   {
14000     next = music_file_info->next;
14001
14002     checked_free(music_file_info->basename);
14003
14004     checked_free(music_file_info->title_header);
14005     checked_free(music_file_info->artist_header);
14006     checked_free(music_file_info->album_header);
14007     checked_free(music_file_info->year_header);
14008     checked_free(music_file_info->played_header);
14009
14010     checked_free(music_file_info->title);
14011     checked_free(music_file_info->artist);
14012     checked_free(music_file_info->album);
14013     checked_free(music_file_info->year);
14014     checked_free(music_file_info->played);
14015
14016     free(music_file_info);
14017
14018     music_file_info = next;
14019   }
14020
14021   new = &music_file_info;
14022
14023   // get (configured or unconfigured) music file info for all levels
14024   for (i = leveldir_current->first_level;
14025        i <= leveldir_current->last_level; i++)
14026   {
14027     int music_nr;
14028
14029     if (levelset.music[i] != MUS_UNDEFINED)
14030     {
14031       // get music file info for configured level music
14032       music_nr = levelset.music[i];
14033     }
14034     else if (num_music_noconf > 0)
14035     {
14036       // get music file info for unconfigured level music
14037       int level_pos = i - leveldir_current->first_level;
14038
14039       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14040     }
14041     else
14042     {
14043       continue;
14044     }
14045
14046     char *basename = getMusicInfoEntryFilename(music_nr);
14047
14048     if (basename == NULL)
14049       continue;
14050
14051     if (!music_info_listed(music_file_info, basename))
14052     {
14053       *new = get_music_file_info(basename, music_nr);
14054
14055       if (*new != NULL)
14056         new = &(*new)->next;
14057     }
14058   }
14059
14060   // get music file info for all remaining configured music files
14061   for (i = 0; i < num_music; i++)
14062   {
14063     music = getMusicListEntry(i);
14064
14065     if (music->filename == NULL)
14066       continue;
14067
14068     if (strEqual(music->filename, UNDEFINED_FILENAME))
14069       continue;
14070
14071     // a configured file may be not recognized as music
14072     if (!FileIsMusic(music->filename))
14073       continue;
14074
14075     if (!music_info_listed(music_file_info, music->filename))
14076     {
14077       *new = get_music_file_info(music->filename, i);
14078
14079       if (*new != NULL)
14080         new = &(*new)->next;
14081     }
14082   }
14083
14084   // get sound file info for all configured sound files
14085   for (i = 0; i < num_sounds; i++)
14086   {
14087     sound = getSoundListEntry(i);
14088
14089     if (sound->filename == NULL)
14090       continue;
14091
14092     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14093       continue;
14094
14095     // a configured file may be not recognized as sound
14096     if (!FileIsSound(sound->filename))
14097       continue;
14098
14099     if (!sound_info_listed(music_file_info, sound->filename))
14100     {
14101       *new = get_sound_file_info(sound->filename, i);
14102       if (*new != NULL)
14103         new = &(*new)->next;
14104     }
14105   }
14106
14107   // add pointers to previous list nodes
14108
14109   struct MusicFileInfo *node = music_file_info;
14110
14111   while (node != NULL)
14112   {
14113     if (node->next)
14114       node->next->prev = node;
14115
14116     node = node->next;
14117   }
14118 }
14119
14120 static void add_helpanim_entry(int element, int action, int direction,
14121                                int delay, int *num_list_entries)
14122 {
14123   struct HelpAnimInfo *new_list_entry;
14124   (*num_list_entries)++;
14125
14126   helpanim_info =
14127     checked_realloc(helpanim_info,
14128                     *num_list_entries * sizeof(struct HelpAnimInfo));
14129   new_list_entry = &helpanim_info[*num_list_entries - 1];
14130
14131   new_list_entry->element = element;
14132   new_list_entry->action = action;
14133   new_list_entry->direction = direction;
14134   new_list_entry->delay = delay;
14135 }
14136
14137 static void print_unknown_token(char *filename, char *token, int token_nr)
14138 {
14139   if (token_nr == 0)
14140   {
14141     Warn("---");
14142     Warn("unknown token(s) found in config file:");
14143     Warn("- config file: '%s'", filename);
14144   }
14145
14146   Warn("- token: '%s'", token);
14147 }
14148
14149 static void print_unknown_token_end(int token_nr)
14150 {
14151   if (token_nr > 0)
14152     Warn("---");
14153 }
14154
14155 void LoadHelpAnimInfo(void)
14156 {
14157   char *filename = getHelpAnimFilename();
14158   SetupFileList *setup_file_list = NULL, *list;
14159   SetupFileHash *element_hash, *action_hash, *direction_hash;
14160   int num_list_entries = 0;
14161   int num_unknown_tokens = 0;
14162   int i;
14163
14164   if (fileExists(filename))
14165     setup_file_list = loadSetupFileList(filename);
14166
14167   if (setup_file_list == NULL)
14168   {
14169     // use reliable default values from static configuration
14170     SetupFileList *insert_ptr;
14171
14172     insert_ptr = setup_file_list =
14173       newSetupFileList(helpanim_config[0].token,
14174                        helpanim_config[0].value);
14175
14176     for (i = 1; helpanim_config[i].token; i++)
14177       insert_ptr = addListEntry(insert_ptr,
14178                                 helpanim_config[i].token,
14179                                 helpanim_config[i].value);
14180   }
14181
14182   element_hash   = newSetupFileHash();
14183   action_hash    = newSetupFileHash();
14184   direction_hash = newSetupFileHash();
14185
14186   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14187     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14188
14189   for (i = 0; i < NUM_ACTIONS; i++)
14190     setHashEntry(action_hash, element_action_info[i].suffix,
14191                  i_to_a(element_action_info[i].value));
14192
14193   // do not store direction index (bit) here, but direction value!
14194   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14195     setHashEntry(direction_hash, element_direction_info[i].suffix,
14196                  i_to_a(1 << element_direction_info[i].value));
14197
14198   for (list = setup_file_list; list != NULL; list = list->next)
14199   {
14200     char *element_token, *action_token, *direction_token;
14201     char *element_value, *action_value, *direction_value;
14202     int delay = atoi(list->value);
14203
14204     if (strEqual(list->token, "end"))
14205     {
14206       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14207
14208       continue;
14209     }
14210
14211     /* first try to break element into element/action/direction parts;
14212        if this does not work, also accept combined "element[.act][.dir]"
14213        elements (like "dynamite.active"), which are unique elements */
14214
14215     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14216     {
14217       element_value = getHashEntry(element_hash, list->token);
14218       if (element_value != NULL)        // element found
14219         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14220                            &num_list_entries);
14221       else
14222       {
14223         // no further suffixes found -- this is not an element
14224         print_unknown_token(filename, list->token, num_unknown_tokens++);
14225       }
14226
14227       continue;
14228     }
14229
14230     // token has format "<prefix>.<something>"
14231
14232     action_token = strchr(list->token, '.');    // suffix may be action ...
14233     direction_token = action_token;             // ... or direction
14234
14235     element_token = getStringCopy(list->token);
14236     *strchr(element_token, '.') = '\0';
14237
14238     element_value = getHashEntry(element_hash, element_token);
14239
14240     if (element_value == NULL)          // this is no element
14241     {
14242       element_value = getHashEntry(element_hash, list->token);
14243       if (element_value != NULL)        // combined element found
14244         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14245                            &num_list_entries);
14246       else
14247         print_unknown_token(filename, list->token, num_unknown_tokens++);
14248
14249       free(element_token);
14250
14251       continue;
14252     }
14253
14254     action_value = getHashEntry(action_hash, action_token);
14255
14256     if (action_value != NULL)           // action found
14257     {
14258       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14259                     &num_list_entries);
14260
14261       free(element_token);
14262
14263       continue;
14264     }
14265
14266     direction_value = getHashEntry(direction_hash, direction_token);
14267
14268     if (direction_value != NULL)        // direction found
14269     {
14270       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14271                          &num_list_entries);
14272
14273       free(element_token);
14274
14275       continue;
14276     }
14277
14278     if (strchr(action_token + 1, '.') == NULL)
14279     {
14280       // no further suffixes found -- this is not an action nor direction
14281
14282       element_value = getHashEntry(element_hash, list->token);
14283       if (element_value != NULL)        // combined element found
14284         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14285                            &num_list_entries);
14286       else
14287         print_unknown_token(filename, list->token, num_unknown_tokens++);
14288
14289       free(element_token);
14290
14291       continue;
14292     }
14293
14294     // token has format "<prefix>.<suffix>.<something>"
14295
14296     direction_token = strchr(action_token + 1, '.');
14297
14298     action_token = getStringCopy(action_token);
14299     *strchr(action_token + 1, '.') = '\0';
14300
14301     action_value = getHashEntry(action_hash, action_token);
14302
14303     if (action_value == NULL)           // this is no action
14304     {
14305       element_value = getHashEntry(element_hash, list->token);
14306       if (element_value != NULL)        // combined element found
14307         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14308                            &num_list_entries);
14309       else
14310         print_unknown_token(filename, list->token, num_unknown_tokens++);
14311
14312       free(element_token);
14313       free(action_token);
14314
14315       continue;
14316     }
14317
14318     direction_value = getHashEntry(direction_hash, direction_token);
14319
14320     if (direction_value != NULL)        // direction found
14321     {
14322       add_helpanim_entry(atoi(element_value), atoi(action_value),
14323                          atoi(direction_value), delay, &num_list_entries);
14324
14325       free(element_token);
14326       free(action_token);
14327
14328       continue;
14329     }
14330
14331     // this is no direction
14332
14333     element_value = getHashEntry(element_hash, list->token);
14334     if (element_value != NULL)          // combined element found
14335       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14336                          &num_list_entries);
14337     else
14338       print_unknown_token(filename, list->token, num_unknown_tokens++);
14339
14340     free(element_token);
14341     free(action_token);
14342   }
14343
14344   print_unknown_token_end(num_unknown_tokens);
14345
14346   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14347   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14348
14349   freeSetupFileList(setup_file_list);
14350   freeSetupFileHash(element_hash);
14351   freeSetupFileHash(action_hash);
14352   freeSetupFileHash(direction_hash);
14353
14354 #if 0
14355   for (i = 0; i < num_list_entries; i++)
14356     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14357           EL_NAME(helpanim_info[i].element),
14358           helpanim_info[i].element,
14359           helpanim_info[i].action,
14360           helpanim_info[i].direction,
14361           helpanim_info[i].delay);
14362 #endif
14363 }
14364
14365 void LoadHelpTextInfo(void)
14366 {
14367   char *filename = getHelpTextFilename();
14368   int i;
14369
14370   if (helptext_info != NULL)
14371   {
14372     freeSetupFileHash(helptext_info);
14373     helptext_info = NULL;
14374   }
14375
14376   if (fileExists(filename))
14377     helptext_info = loadSetupFileHash(filename);
14378
14379   if (helptext_info == NULL)
14380   {
14381     // use reliable default values from static configuration
14382     helptext_info = newSetupFileHash();
14383
14384     for (i = 0; helptext_config[i].token; i++)
14385       setHashEntry(helptext_info,
14386                    helptext_config[i].token,
14387                    helptext_config[i].value);
14388   }
14389
14390 #if 0
14391   BEGIN_HASH_ITERATION(helptext_info, itr)
14392   {
14393     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14394           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14395   }
14396   END_HASH_ITERATION(hash, itr)
14397 #endif
14398 }
14399
14400
14401 // ----------------------------------------------------------------------------
14402 // convert levels
14403 // ----------------------------------------------------------------------------
14404
14405 #define MAX_NUM_CONVERT_LEVELS          1000
14406
14407 void ConvertLevels(void)
14408 {
14409   static LevelDirTree *convert_leveldir = NULL;
14410   static int convert_level_nr = -1;
14411   static int num_levels_handled = 0;
14412   static int num_levels_converted = 0;
14413   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14414   int i;
14415
14416   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14417                                                global.convert_leveldir);
14418
14419   if (convert_leveldir == NULL)
14420     Fail("no such level identifier: '%s'", global.convert_leveldir);
14421
14422   leveldir_current = convert_leveldir;
14423
14424   if (global.convert_level_nr != -1)
14425   {
14426     convert_leveldir->first_level = global.convert_level_nr;
14427     convert_leveldir->last_level  = global.convert_level_nr;
14428   }
14429
14430   convert_level_nr = convert_leveldir->first_level;
14431
14432   PrintLine("=", 79);
14433   Print("Converting levels\n");
14434   PrintLine("-", 79);
14435   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14436   Print("Level series name:       '%s'\n", convert_leveldir->name);
14437   Print("Level series author:     '%s'\n", convert_leveldir->author);
14438   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14439   PrintLine("=", 79);
14440   Print("\n");
14441
14442   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14443     levels_failed[i] = FALSE;
14444
14445   while (convert_level_nr <= convert_leveldir->last_level)
14446   {
14447     char *level_filename;
14448     boolean new_level;
14449
14450     level_nr = convert_level_nr++;
14451
14452     Print("Level %03d: ", level_nr);
14453
14454     LoadLevel(level_nr);
14455     if (level.no_level_file || level.no_valid_file)
14456     {
14457       Print("(no level)\n");
14458       continue;
14459     }
14460
14461     Print("converting level ... ");
14462
14463 #if 0
14464     // special case: conversion of some EMC levels as requested by ACME
14465     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14466 #endif
14467
14468     level_filename = getDefaultLevelFilename(level_nr);
14469     new_level = !fileExists(level_filename);
14470
14471     if (new_level)
14472     {
14473       SaveLevel(level_nr);
14474
14475       num_levels_converted++;
14476
14477       Print("converted.\n");
14478     }
14479     else
14480     {
14481       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14482         levels_failed[level_nr] = TRUE;
14483
14484       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14485     }
14486
14487     num_levels_handled++;
14488   }
14489
14490   Print("\n");
14491   PrintLine("=", 79);
14492   Print("Number of levels handled: %d\n", num_levels_handled);
14493   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14494          (num_levels_handled ?
14495           num_levels_converted * 100 / num_levels_handled : 0));
14496   PrintLine("-", 79);
14497   Print("Summary (for automatic parsing by scripts):\n");
14498   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14499          convert_leveldir->identifier, num_levels_converted,
14500          num_levels_handled,
14501          (num_levels_handled ?
14502           num_levels_converted * 100 / num_levels_handled : 0));
14503
14504   if (num_levels_handled != num_levels_converted)
14505   {
14506     Print(", FAILED:");
14507     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14508       if (levels_failed[i])
14509         Print(" %03d", i);
14510   }
14511
14512   Print("\n");
14513   PrintLine("=", 79);
14514
14515   CloseAllAndExit(0);
14516 }
14517
14518
14519 // ----------------------------------------------------------------------------
14520 // create and save images for use in level sketches (raw BMP format)
14521 // ----------------------------------------------------------------------------
14522
14523 void CreateLevelSketchImages(void)
14524 {
14525   Bitmap *bitmap1;
14526   Bitmap *bitmap2;
14527   int i;
14528
14529   InitElementPropertiesGfxElement();
14530
14531   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14532   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14533
14534   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14535   {
14536     int element = getMappedElement(i);
14537     char basename1[16];
14538     char basename2[16];
14539     char *filename1;
14540     char *filename2;
14541
14542     sprintf(basename1, "%04d.bmp", i);
14543     sprintf(basename2, "%04ds.bmp", i);
14544
14545     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14546     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14547
14548     DrawSizedElement(0, 0, element, TILESIZE);
14549     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14550
14551     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14552       Fail("cannot save level sketch image file '%s'", filename1);
14553
14554     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14555     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14556
14557     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14558       Fail("cannot save level sketch image file '%s'", filename2);
14559
14560     free(filename1);
14561     free(filename2);
14562
14563     // create corresponding SQL statements (for normal and small images)
14564     if (i < 1000)
14565     {
14566       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14567       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14568     }
14569
14570     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14571     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14572
14573     // optional: create content for forum level sketch demonstration post
14574     if (options.debug)
14575       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14576   }
14577
14578   FreeBitmap(bitmap1);
14579   FreeBitmap(bitmap2);
14580
14581   if (options.debug)
14582     fprintf(stderr, "\n");
14583
14584   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14585
14586   CloseAllAndExit(0);
14587 }
14588
14589
14590 // ----------------------------------------------------------------------------
14591 // create and save images for element collecting animations (raw BMP format)
14592 // ----------------------------------------------------------------------------
14593
14594 static boolean createCollectImage(int element)
14595 {
14596   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14597 }
14598
14599 void CreateCollectElementImages(void)
14600 {
14601   int i, j;
14602   int num_steps = 8;
14603   int anim_frames = num_steps - 1;
14604   int tile_size = TILESIZE;
14605   int anim_width  = tile_size * anim_frames;
14606   int anim_height = tile_size;
14607   int num_collect_images = 0;
14608   int pos_collect_images = 0;
14609
14610   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14611     if (createCollectImage(i))
14612       num_collect_images++;
14613
14614   Info("Creating %d element collecting animation images ...",
14615        num_collect_images);
14616
14617   int dst_width  = anim_width * 2;
14618   int dst_height = anim_height * num_collect_images / 2;
14619   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14620   char *basename_bmp = "RocksCollect.bmp";
14621   char *basename_png = "RocksCollect.png";
14622   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14623   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14624   int len_filename_bmp = strlen(filename_bmp);
14625   int len_filename_png = strlen(filename_png);
14626   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14627   char cmd_convert[max_command_len];
14628
14629   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14630            filename_bmp,
14631            filename_png);
14632
14633   // force using RGBA surface for destination bitmap
14634   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14635                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14636
14637   dst_bitmap->surface =
14638     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14639
14640   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14641   {
14642     if (!createCollectImage(i))
14643       continue;
14644
14645     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14646     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14647     int graphic = el2img(i);
14648     char *token_name = element_info[i].token_name;
14649     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14650     Bitmap *src_bitmap;
14651     int src_x, src_y;
14652
14653     Info("- creating collecting image for '%s' ...", token_name);
14654
14655     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14656
14657     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14658                tile_size, tile_size, 0, 0);
14659
14660     // force using RGBA surface for temporary bitmap (using transparent black)
14661     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14662                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14663
14664     tmp_bitmap->surface =
14665       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14666
14667     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14668
14669     for (j = 0; j < anim_frames; j++)
14670     {
14671       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14672       int frame_size = frame_size_final * num_steps;
14673       int offset = (tile_size - frame_size_final) / 2;
14674       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14675
14676       while (frame_size > frame_size_final)
14677       {
14678         frame_size /= 2;
14679
14680         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14681
14682         FreeBitmap(frame_bitmap);
14683
14684         frame_bitmap = half_bitmap;
14685       }
14686
14687       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14688                        frame_size_final, frame_size_final,
14689                        dst_x + j * tile_size + offset, dst_y + offset);
14690
14691       FreeBitmap(frame_bitmap);
14692     }
14693
14694     tmp_bitmap->surface_masked = NULL;
14695
14696     FreeBitmap(tmp_bitmap);
14697
14698     pos_collect_images++;
14699   }
14700
14701   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14702     Fail("cannot save element collecting image file '%s'", filename_bmp);
14703
14704   FreeBitmap(dst_bitmap);
14705
14706   Info("Converting image file from BMP to PNG ...");
14707
14708   if (system(cmd_convert) != 0)
14709     Fail("converting image file failed");
14710
14711   unlink(filename_bmp);
14712
14713   Info("Done.");
14714
14715   CloseAllAndExit(0);
14716 }
14717
14718
14719 // ----------------------------------------------------------------------------
14720 // create and save images for custom and group elements (raw BMP format)
14721 // ----------------------------------------------------------------------------
14722
14723 void CreateCustomElementImages(char *directory)
14724 {
14725   char *src_basename = "RocksCE-template.ilbm";
14726   char *dst_basename = "RocksCE.bmp";
14727   char *src_filename = getPath2(directory, src_basename);
14728   char *dst_filename = getPath2(directory, dst_basename);
14729   Bitmap *src_bitmap;
14730   Bitmap *bitmap;
14731   int yoffset_ce = 0;
14732   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14733   int i;
14734
14735   InitVideoDefaults();
14736
14737   ReCreateBitmap(&backbuffer, video.width, video.height);
14738
14739   src_bitmap = LoadImage(src_filename);
14740
14741   bitmap = CreateBitmap(TILEX * 16 * 2,
14742                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14743                         DEFAULT_DEPTH);
14744
14745   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14746   {
14747     int x = i % 16;
14748     int y = i / 16;
14749     int ii = i + 1;
14750     int j;
14751
14752     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14753                TILEX * x, TILEY * y + yoffset_ce);
14754
14755     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14756                TILEX, TILEY,
14757                TILEX * x + TILEX * 16,
14758                TILEY * y + yoffset_ce);
14759
14760     for (j = 2; j >= 0; j--)
14761     {
14762       int c = ii % 10;
14763
14764       BlitBitmap(src_bitmap, bitmap,
14765                  TILEX + c * 7, 0, 6, 10,
14766                  TILEX * x + 6 + j * 7,
14767                  TILEY * y + 11 + yoffset_ce);
14768
14769       BlitBitmap(src_bitmap, bitmap,
14770                  TILEX + c * 8, TILEY, 6, 10,
14771                  TILEX * 16 + TILEX * x + 6 + j * 8,
14772                  TILEY * y + 10 + yoffset_ce);
14773
14774       ii /= 10;
14775     }
14776   }
14777
14778   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14779   {
14780     int x = i % 16;
14781     int y = i / 16;
14782     int ii = i + 1;
14783     int j;
14784
14785     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14786                TILEX * x, TILEY * y + yoffset_ge);
14787
14788     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14789                TILEX, TILEY,
14790                TILEX * x + TILEX * 16,
14791                TILEY * y + yoffset_ge);
14792
14793     for (j = 1; j >= 0; j--)
14794     {
14795       int c = ii % 10;
14796
14797       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14798                  TILEX * x + 6 + j * 10,
14799                  TILEY * y + 11 + yoffset_ge);
14800
14801       BlitBitmap(src_bitmap, bitmap,
14802                  TILEX + c * 8, TILEY + 12, 6, 10,
14803                  TILEX * 16 + TILEX * x + 10 + j * 8,
14804                  TILEY * y + 10 + yoffset_ge);
14805
14806       ii /= 10;
14807     }
14808   }
14809
14810   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14811     Fail("cannot save CE graphics file '%s'", dst_filename);
14812
14813   FreeBitmap(bitmap);
14814
14815   CloseAllAndExit(0);
14816 }