fixed element default settings for acid (BD engine)
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
749     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
754     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
759     &li.bd_slime_eats_element_2,        EL_BD_ROCK
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
764     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
769     &li.bd_slime_eats_element_3,        EL_BD_NUT
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
774     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
775   },
776
777   {
778     EL_BD_ACID,                         -1,
779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
780     &li.bd_acid_eats_element,           EL_BD_SAND
781   },
782   {
783     EL_BD_ACID,                         -1,
784     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
785     &li.bd_acid_spread_rate,            3
786   },
787   {
788     EL_BD_ACID,                         -1,
789     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
790     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
791   },
792
793   {
794     EL_BD_BITER,                        -1,
795     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
796     &li.bd_biter_move_delay,            0
797   },
798   {
799     EL_BD_BITER,                        -1,
800     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
801     &li.bd_biter_eats_element,          EL_BD_DIAMOND
802   },
803
804   {
805     EL_BD_BLADDER,                      -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
808   },
809
810   {
811     EL_BD_EXPANDABLE_WALL_ANY,          -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.bd_change_expanding_wall,       FALSE
814   },
815
816   {
817     EL_BD_REPLICATOR,                   -1,
818     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
819     &li.bd_replicators_active,          TRUE
820   },
821   {
822     EL_BD_REPLICATOR,                   -1,
823     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
824     &li.bd_replicator_create_delay,     4
825   },
826
827   {
828     EL_BD_CONVEYOR_LEFT,                -1,
829     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
830     &li.bd_conveyor_belts_active,       TRUE
831   },
832   {
833     EL_BD_CONVEYOR_LEFT,                -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
835     &li.bd_conveyor_belts_changed,      FALSE
836   },
837
838   {
839     EL_BD_WATER,                        -1,
840     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
841     &li.bd_water_cannot_flow_down,      FALSE
842   },
843
844   {
845     EL_BD_NUT,                          -1,
846     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
847     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
848   },
849
850   {
851     EL_BD_PNEUMATIC_HAMMER,             -1,
852     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
853     &li.bd_hammer_walls_break_delay,    5
854   },
855   {
856     EL_BD_PNEUMATIC_HAMMER,             -1,
857     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
858     &li.bd_hammer_walls_reappear,       FALSE
859   },
860   {
861     EL_BD_PNEUMATIC_HAMMER,             -1,
862     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
863     &li.bd_hammer_walls_reappear_delay, 100
864   },
865
866   // (the following values are related to various game elements)
867
868   {
869     EL_EMERALD,                         -1,
870     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
871     &li.score[SC_EMERALD],              10
872   },
873
874   {
875     EL_DIAMOND,                         -1,
876     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
877     &li.score[SC_DIAMOND],              10
878   },
879
880   {
881     EL_BUG,                             -1,
882     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
883     &li.score[SC_BUG],                  10
884   },
885
886   {
887     EL_SPACESHIP,                       -1,
888     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
889     &li.score[SC_SPACESHIP],            10
890   },
891
892   {
893     EL_PACMAN,                          -1,
894     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
895     &li.score[SC_PACMAN],               10
896   },
897
898   {
899     EL_NUT,                             -1,
900     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
901     &li.score[SC_NUT],                  10
902   },
903
904   {
905     EL_DYNAMITE,                        -1,
906     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
907     &li.score[SC_DYNAMITE],             10
908   },
909
910   {
911     EL_KEY_1,                           -1,
912     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
913     &li.score[SC_KEY],                  10
914   },
915
916   {
917     EL_PEARL,                           -1,
918     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
919     &li.score[SC_PEARL],                10
920   },
921
922   {
923     EL_CRYSTAL,                         -1,
924     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
925     &li.score[SC_CRYSTAL],              10
926   },
927
928   // (amoeba values used by R'n'D game engine only)
929   {
930     EL_BD_AMOEBA,                       -1,
931     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
932     &li.amoeba_content,                 EL_DIAMOND
933   },
934   {
935     EL_BD_AMOEBA,                       -1,
936     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
937     &li.amoeba_speed,                   10
938   },
939   {
940     EL_BD_AMOEBA,                       -1,
941     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
942     &li.grow_into_diggable,             TRUE
943   },
944   // (amoeba values used by BD game engine only)
945   {
946     EL_BD_AMOEBA,                       -1,
947     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
948     &li.bd_amoeba_wait_for_hatching,    FALSE
949   },
950   {
951     EL_BD_AMOEBA,                       -1,
952     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
953     &li.bd_amoeba_start_immediately,    TRUE
954   },
955   {
956     EL_BD_AMOEBA,                       -1,
957     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
958     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
959   },
960   {
961     EL_BD_AMOEBA,                       -1,
962     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
963     &li.bd_amoeba_threshold_too_big,    200
964   },
965   {
966     EL_BD_AMOEBA,                       -1,
967     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
968     &li.bd_amoeba_slow_growth_time,     200
969   },
970   {
971     EL_BD_AMOEBA,                       -1,
972     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
973     &li.bd_amoeba_slow_growth_rate,     3
974   },
975   {
976     EL_BD_AMOEBA,                       -1,
977     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
978     &li.bd_amoeba_fast_growth_rate,     25
979   },
980   {
981     EL_BD_AMOEBA,                       -1,
982     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
983     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
984   },
985   {
986     EL_BD_AMOEBA,                       -1,
987     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
988     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
989   },
990
991   {
992     EL_BD_AMOEBA_2,                     -1,
993     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
994     &li.bd_amoeba_2_threshold_too_big,  200
995   },
996   {
997     EL_BD_AMOEBA_2,                     -1,
998     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
999     &li.bd_amoeba_2_slow_growth_time,   200
1000   },
1001   {
1002     EL_BD_AMOEBA_2,                     -1,
1003     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1004     &li.bd_amoeba_2_slow_growth_rate,   3
1005   },
1006   {
1007     EL_BD_AMOEBA_2,                     -1,
1008     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1009     &li.bd_amoeba_2_fast_growth_rate,   25
1010   },
1011   {
1012     EL_BD_AMOEBA_2,                     -1,
1013     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1014     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1015   },
1016   {
1017     EL_BD_AMOEBA_2,                     -1,
1018     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1019     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1020   },
1021   {
1022     EL_BD_AMOEBA_2,                     -1,
1023     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1024     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1025   },
1026   {
1027     EL_BD_AMOEBA_2,                     -1,
1028     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1029     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1030   },
1031
1032   {
1033     EL_YAMYAM,                          -1,
1034     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1035     &li.yamyam_content,                 EL_ROCK, NULL,
1036     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1037   },
1038   {
1039     EL_YAMYAM,                          -1,
1040     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1041     &li.score[SC_YAMYAM],               10
1042   },
1043
1044   {
1045     EL_ROBOT,                           -1,
1046     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1047     &li.score[SC_ROBOT],                10
1048   },
1049   {
1050     EL_ROBOT,                           -1,
1051     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1052     &li.slurp_score,                    10
1053   },
1054
1055   {
1056     EL_ROBOT_WHEEL,                     -1,
1057     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1058     &li.time_wheel,                     10
1059   },
1060
1061   {
1062     EL_MAGIC_WALL,                      -1,
1063     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1064     &li.time_magic_wall,                10
1065   },
1066
1067   {
1068     EL_GAME_OF_LIFE,                    -1,
1069     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1070     &li.game_of_life[0],                2
1071   },
1072   {
1073     EL_GAME_OF_LIFE,                    -1,
1074     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1075     &li.game_of_life[1],                3
1076   },
1077   {
1078     EL_GAME_OF_LIFE,                    -1,
1079     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1080     &li.game_of_life[2],                3
1081   },
1082   {
1083     EL_GAME_OF_LIFE,                    -1,
1084     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1085     &li.game_of_life[3],                3
1086   },
1087   {
1088     EL_GAME_OF_LIFE,                    -1,
1089     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1090     &li.use_life_bugs,                  FALSE
1091   },
1092
1093   {
1094     EL_BIOMAZE,                         -1,
1095     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1096     &li.biomaze[0],                     2
1097   },
1098   {
1099     EL_BIOMAZE,                         -1,
1100     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1101     &li.biomaze[1],                     3
1102   },
1103   {
1104     EL_BIOMAZE,                         -1,
1105     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1106     &li.biomaze[2],                     3
1107   },
1108   {
1109     EL_BIOMAZE,                         -1,
1110     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1111     &li.biomaze[3],                     3
1112   },
1113
1114   {
1115     EL_TIMEGATE_SWITCH,                 -1,
1116     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1117     &li.time_timegate,                  10
1118   },
1119
1120   {
1121     EL_LIGHT_SWITCH_ACTIVE,             -1,
1122     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1123     &li.time_light,                     10
1124   },
1125
1126   {
1127     EL_SHIELD_NORMAL,                   -1,
1128     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1129     &li.shield_normal_time,             10
1130   },
1131   {
1132     EL_SHIELD_NORMAL,                   -1,
1133     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1134     &li.score[SC_SHIELD],               10
1135   },
1136
1137   {
1138     EL_SHIELD_DEADLY,                   -1,
1139     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1140     &li.shield_deadly_time,             10
1141   },
1142   {
1143     EL_SHIELD_DEADLY,                   -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1145     &li.score[SC_SHIELD],               10
1146   },
1147
1148   {
1149     EL_EXTRA_TIME,                      -1,
1150     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1151     &li.extra_time,                     10
1152   },
1153   {
1154     EL_EXTRA_TIME,                      -1,
1155     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1156     &li.extra_time_score,               10
1157   },
1158
1159   {
1160     EL_TIME_ORB_FULL,                   -1,
1161     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1162     &li.time_orb_time,                  10
1163   },
1164   {
1165     EL_TIME_ORB_FULL,                   -1,
1166     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1167     &li.use_time_orb_bug,               FALSE
1168   },
1169
1170   {
1171     EL_SPRING,                          -1,
1172     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1173     &li.use_spring_bug,                 FALSE
1174   },
1175
1176   {
1177     EL_EMC_ANDROID,                     -1,
1178     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1179     &li.android_move_time,              10
1180   },
1181   {
1182     EL_EMC_ANDROID,                     -1,
1183     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1184     &li.android_clone_time,             10
1185   },
1186   {
1187     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1188     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1189     &li.android_clone_element[0],       EL_EMPTY, NULL,
1190     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1191   },
1192   {
1193     EL_EMC_ANDROID,                     -1,
1194     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1195     &li.android_clone_element[0],       EL_EMPTY, NULL,
1196     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1197   },
1198
1199   {
1200     EL_EMC_LENSES,                      -1,
1201     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1202     &li.lenses_score,                   10
1203   },
1204   {
1205     EL_EMC_LENSES,                      -1,
1206     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1207     &li.lenses_time,                    10
1208   },
1209
1210   {
1211     EL_EMC_MAGNIFIER,                   -1,
1212     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1213     &li.magnify_score,                  10
1214   },
1215   {
1216     EL_EMC_MAGNIFIER,                   -1,
1217     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1218     &li.magnify_time,                   10
1219   },
1220
1221   {
1222     EL_EMC_MAGIC_BALL,                  -1,
1223     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1224     &li.ball_time,                      10
1225   },
1226   {
1227     EL_EMC_MAGIC_BALL,                  -1,
1228     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1229     &li.ball_random,                    FALSE
1230   },
1231   {
1232     EL_EMC_MAGIC_BALL,                  -1,
1233     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1234     &li.ball_active_initial,            FALSE
1235   },
1236   {
1237     EL_EMC_MAGIC_BALL,                  -1,
1238     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1239     &li.ball_content,                   EL_EMPTY, NULL,
1240     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1241   },
1242
1243   {
1244     EL_SOKOBAN_FIELD_EMPTY,             -1,
1245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1246     &li.sb_fields_needed,               TRUE
1247   },
1248
1249   {
1250     EL_SOKOBAN_OBJECT,                  -1,
1251     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1252     &li.sb_objects_needed,              TRUE
1253   },
1254
1255   {
1256     EL_MM_MCDUFFIN,                     -1,
1257     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1258     &li.mm_laser_red,                   FALSE
1259   },
1260   {
1261     EL_MM_MCDUFFIN,                     -1,
1262     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1263     &li.mm_laser_green,                 FALSE
1264   },
1265   {
1266     EL_MM_MCDUFFIN,                     -1,
1267     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1268     &li.mm_laser_blue,                  TRUE
1269   },
1270
1271   {
1272     EL_DF_LASER,                        -1,
1273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1274     &li.df_laser_red,                   TRUE
1275   },
1276   {
1277     EL_DF_LASER,                        -1,
1278     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1279     &li.df_laser_green,                 TRUE
1280   },
1281   {
1282     EL_DF_LASER,                        -1,
1283     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1284     &li.df_laser_blue,                  FALSE
1285   },
1286
1287   {
1288     EL_MM_FUSE_ACTIVE,                  -1,
1289     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1290     &li.mm_time_fuse,                   25
1291   },
1292   {
1293     EL_MM_BOMB,                         -1,
1294     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1295     &li.mm_time_bomb,                   75
1296   },
1297
1298   {
1299     EL_MM_GRAY_BALL,                    -1,
1300     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1301     &li.mm_time_ball,                   75
1302   },
1303   {
1304     EL_MM_GRAY_BALL,                    -1,
1305     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1306     &li.mm_ball_choice_mode,            ANIM_RANDOM
1307   },
1308   {
1309     EL_MM_GRAY_BALL,                    -1,
1310     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1311     &li.mm_ball_content,                EL_EMPTY, NULL,
1312     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1313   },
1314   {
1315     EL_MM_GRAY_BALL,                    -1,
1316     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1317     &li.rotate_mm_ball_content,         TRUE
1318   },
1319   {
1320     EL_MM_GRAY_BALL,                    -1,
1321     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1322     &li.explode_mm_ball,                FALSE
1323   },
1324
1325   {
1326     EL_MM_STEEL_BLOCK,                  -1,
1327     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1328     &li.mm_time_block,                  75
1329   },
1330   {
1331     EL_MM_LIGHTBALL,                    -1,
1332     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1333     &li.score[SC_ELEM_BONUS],           10
1334   },
1335
1336   {
1337     -1,                                 -1,
1338     -1,                                 -1,
1339     NULL,                               -1
1340   }
1341 };
1342
1343 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1344 {
1345   {
1346     -1,                                 -1,
1347     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1348     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1349   },
1350   {
1351     -1,                                 -1,
1352     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1353     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1354   },
1355
1356   {
1357     -1,                                 -1,
1358     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1359     &xx_envelope.autowrap,              FALSE
1360   },
1361   {
1362     -1,                                 -1,
1363     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1364     &xx_envelope.centered,              FALSE
1365   },
1366
1367   {
1368     -1,                                 -1,
1369     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1370     &xx_envelope.text,                  -1, NULL,
1371     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1372     &xx_default_string_empty[0]
1373   },
1374
1375   {
1376     -1,                                 -1,
1377     -1,                                 -1,
1378     NULL,                               -1
1379   }
1380 };
1381
1382 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1383 {
1384   {
1385     -1,                                 -1,
1386     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1387     &xx_ei.description[0],              -1,
1388     &yy_ei.description[0],
1389     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1390     &xx_default_description[0]
1391   },
1392
1393   {
1394     -1,                                 -1,
1395     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1396     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1397     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1398   },
1399 #if ENABLE_RESERVED_CODE
1400   // (reserved for later use)
1401   {
1402     -1,                                 -1,
1403     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1404     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1405     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1406   },
1407 #endif
1408
1409   {
1410     -1,                                 -1,
1411     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1412     &xx_ei.use_gfx_element,             FALSE,
1413     &yy_ei.use_gfx_element
1414   },
1415   {
1416     -1,                                 -1,
1417     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1418     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1419     &yy_ei.gfx_element_initial
1420   },
1421
1422   {
1423     -1,                                 -1,
1424     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1425     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1426     &yy_ei.access_direction
1427   },
1428
1429   {
1430     -1,                                 -1,
1431     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1432     &xx_ei.collect_score_initial,       10,
1433     &yy_ei.collect_score_initial
1434   },
1435   {
1436     -1,                                 -1,
1437     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1438     &xx_ei.collect_count_initial,       1,
1439     &yy_ei.collect_count_initial
1440   },
1441
1442   {
1443     -1,                                 -1,
1444     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1445     &xx_ei.ce_value_fixed_initial,      0,
1446     &yy_ei.ce_value_fixed_initial
1447   },
1448   {
1449     -1,                                 -1,
1450     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1451     &xx_ei.ce_value_random_initial,     0,
1452     &yy_ei.ce_value_random_initial
1453   },
1454   {
1455     -1,                                 -1,
1456     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1457     &xx_ei.use_last_ce_value,           FALSE,
1458     &yy_ei.use_last_ce_value
1459   },
1460
1461   {
1462     -1,                                 -1,
1463     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1464     &xx_ei.push_delay_fixed,            8,
1465     &yy_ei.push_delay_fixed
1466   },
1467   {
1468     -1,                                 -1,
1469     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1470     &xx_ei.push_delay_random,           8,
1471     &yy_ei.push_delay_random
1472   },
1473   {
1474     -1,                                 -1,
1475     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1476     &xx_ei.drop_delay_fixed,            0,
1477     &yy_ei.drop_delay_fixed
1478   },
1479   {
1480     -1,                                 -1,
1481     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1482     &xx_ei.drop_delay_random,           0,
1483     &yy_ei.drop_delay_random
1484   },
1485   {
1486     -1,                                 -1,
1487     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1488     &xx_ei.move_delay_fixed,            0,
1489     &yy_ei.move_delay_fixed
1490   },
1491   {
1492     -1,                                 -1,
1493     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1494     &xx_ei.move_delay_random,           0,
1495     &yy_ei.move_delay_random
1496   },
1497   {
1498     -1,                                 -1,
1499     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1500     &xx_ei.step_delay_fixed,            0,
1501     &yy_ei.step_delay_fixed
1502   },
1503   {
1504     -1,                                 -1,
1505     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1506     &xx_ei.step_delay_random,           0,
1507     &yy_ei.step_delay_random
1508   },
1509
1510   {
1511     -1,                                 -1,
1512     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1513     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1514     &yy_ei.move_pattern
1515   },
1516   {
1517     -1,                                 -1,
1518     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1519     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1520     &yy_ei.move_direction_initial
1521   },
1522   {
1523     -1,                                 -1,
1524     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1525     &xx_ei.move_stepsize,               TILEX / 8,
1526     &yy_ei.move_stepsize
1527   },
1528
1529   {
1530     -1,                                 -1,
1531     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1532     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1533     &yy_ei.move_enter_element
1534   },
1535   {
1536     -1,                                 -1,
1537     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1538     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1539     &yy_ei.move_leave_element
1540   },
1541   {
1542     -1,                                 -1,
1543     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1544     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1545     &yy_ei.move_leave_type
1546   },
1547
1548   {
1549     -1,                                 -1,
1550     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1551     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1552     &yy_ei.slippery_type
1553   },
1554
1555   {
1556     -1,                                 -1,
1557     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1558     &xx_ei.explosion_type,              EXPLODES_3X3,
1559     &yy_ei.explosion_type
1560   },
1561   {
1562     -1,                                 -1,
1563     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1564     &xx_ei.explosion_delay,             16,
1565     &yy_ei.explosion_delay
1566   },
1567   {
1568     -1,                                 -1,
1569     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1570     &xx_ei.ignition_delay,              8,
1571     &yy_ei.ignition_delay
1572   },
1573
1574   {
1575     -1,                                 -1,
1576     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1577     &xx_ei.content,                     EL_EMPTY_SPACE,
1578     &yy_ei.content,
1579     &xx_num_contents,                   1, 1
1580   },
1581
1582   // ---------- "num_change_pages" must be the last entry ---------------------
1583
1584   {
1585     -1,                                 SAVE_CONF_ALWAYS,
1586     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1587     &xx_ei.num_change_pages,            1,
1588     &yy_ei.num_change_pages
1589   },
1590
1591   {
1592     -1,                                 -1,
1593     -1,                                 -1,
1594     NULL,                               -1,
1595     NULL
1596   }
1597 };
1598
1599 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1600 {
1601   // ---------- "current_change_page" must be the first entry -----------------
1602
1603   {
1604     -1,                                 SAVE_CONF_ALWAYS,
1605     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1606     &xx_current_change_page,            -1
1607   },
1608
1609   // ---------- (the remaining entries can be in any order) -------------------
1610
1611   {
1612     -1,                                 -1,
1613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1614     &xx_change.can_change,              FALSE
1615   },
1616
1617   {
1618     -1,                                 -1,
1619     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1620     &xx_event_bits[0],                  0
1621   },
1622   {
1623     -1,                                 -1,
1624     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1625     &xx_event_bits[1],                  0
1626   },
1627
1628   {
1629     -1,                                 -1,
1630     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1631     &xx_change.trigger_player,          CH_PLAYER_ANY
1632   },
1633   {
1634     -1,                                 -1,
1635     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1636     &xx_change.trigger_side,            CH_SIDE_ANY
1637   },
1638   {
1639     -1,                                 -1,
1640     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1641     &xx_change.trigger_page,            CH_PAGE_ANY
1642   },
1643
1644   {
1645     -1,                                 -1,
1646     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1647     &xx_change.target_element,          EL_EMPTY_SPACE
1648   },
1649
1650   {
1651     -1,                                 -1,
1652     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1653     &xx_change.delay_fixed,             0
1654   },
1655   {
1656     -1,                                 -1,
1657     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1658     &xx_change.delay_random,            0
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1663     &xx_change.delay_frames,            FRAMES_PER_SECOND
1664   },
1665
1666   {
1667     -1,                                 -1,
1668     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1669     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1670   },
1671
1672   {
1673     -1,                                 -1,
1674     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1675     &xx_change.explode,                 FALSE
1676   },
1677   {
1678     -1,                                 -1,
1679     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1680     &xx_change.use_target_content,      FALSE
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1685     &xx_change.only_if_complete,        FALSE
1686   },
1687   {
1688     -1,                                 -1,
1689     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1690     &xx_change.use_random_replace,      FALSE
1691   },
1692   {
1693     -1,                                 -1,
1694     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1695     &xx_change.random_percentage,       100
1696   },
1697   {
1698     -1,                                 -1,
1699     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1700     &xx_change.replace_when,            CP_WHEN_EMPTY
1701   },
1702
1703   {
1704     -1,                                 -1,
1705     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1706     &xx_change.has_action,              FALSE
1707   },
1708   {
1709     -1,                                 -1,
1710     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1711     &xx_change.action_type,             CA_NO_ACTION
1712   },
1713   {
1714     -1,                                 -1,
1715     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1716     &xx_change.action_mode,             CA_MODE_UNDEFINED
1717   },
1718   {
1719     -1,                                 -1,
1720     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1721     &xx_change.action_arg,              CA_ARG_UNDEFINED
1722   },
1723
1724   {
1725     -1,                                 -1,
1726     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1727     &xx_change.action_element,          EL_EMPTY_SPACE
1728   },
1729
1730   {
1731     -1,                                 -1,
1732     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1733     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1734     &xx_num_contents,                   1, 1
1735   },
1736
1737   {
1738     -1,                                 -1,
1739     -1,                                 -1,
1740     NULL,                               -1
1741   }
1742 };
1743
1744 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1745 {
1746   {
1747     -1,                                 -1,
1748     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1749     &xx_ei.description[0],              -1, NULL,
1750     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1751     &xx_default_description[0]
1752   },
1753
1754   {
1755     -1,                                 -1,
1756     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1757     &xx_ei.use_gfx_element,             FALSE
1758   },
1759   {
1760     -1,                                 -1,
1761     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1762     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1763   },
1764
1765   {
1766     -1,                                 -1,
1767     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1768     &xx_group.choice_mode,              ANIM_RANDOM
1769   },
1770
1771   {
1772     -1,                                 -1,
1773     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1774     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1775     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1776   },
1777
1778   {
1779     -1,                                 -1,
1780     -1,                                 -1,
1781     NULL,                               -1
1782   }
1783 };
1784
1785 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1786 {
1787   {
1788     -1,                                 -1,
1789     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1790     &xx_ei.use_gfx_element,             FALSE
1791   },
1792   {
1793     -1,                                 -1,
1794     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1795     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1796   },
1797
1798   {
1799     -1,                                 -1,
1800     -1,                                 -1,
1801     NULL,                               -1
1802   }
1803 };
1804
1805 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1806 {
1807   {
1808     EL_PLAYER_1,                        -1,
1809     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1810     &li.block_snap_field,               TRUE
1811   },
1812   {
1813     EL_PLAYER_1,                        -1,
1814     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1815     &li.continuous_snapping,            TRUE
1816   },
1817   {
1818     EL_PLAYER_1,                        -1,
1819     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1820     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1821   },
1822   {
1823     EL_PLAYER_1,                        -1,
1824     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1825     &li.use_start_element[0],           FALSE
1826   },
1827   {
1828     EL_PLAYER_1,                        -1,
1829     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1830     &li.start_element[0],               EL_PLAYER_1
1831   },
1832   {
1833     EL_PLAYER_1,                        -1,
1834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1835     &li.use_artwork_element[0],         FALSE
1836   },
1837   {
1838     EL_PLAYER_1,                        -1,
1839     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1840     &li.artwork_element[0],             EL_PLAYER_1
1841   },
1842   {
1843     EL_PLAYER_1,                        -1,
1844     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1845     &li.use_explosion_element[0],       FALSE
1846   },
1847   {
1848     EL_PLAYER_1,                        -1,
1849     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1850     &li.explosion_element[0],           EL_PLAYER_1
1851   },
1852
1853   {
1854     -1,                                 -1,
1855     -1,                                 -1,
1856     NULL,                               -1
1857   }
1858 };
1859
1860 static struct
1861 {
1862   int filetype;
1863   char *id;
1864 }
1865 filetype_id_list[] =
1866 {
1867   { LEVEL_FILE_TYPE_RND,        "RND"   },
1868   { LEVEL_FILE_TYPE_BD,         "BD"    },
1869   { LEVEL_FILE_TYPE_EM,         "EM"    },
1870   { LEVEL_FILE_TYPE_SP,         "SP"    },
1871   { LEVEL_FILE_TYPE_DX,         "DX"    },
1872   { LEVEL_FILE_TYPE_SB,         "SB"    },
1873   { LEVEL_FILE_TYPE_DC,         "DC"    },
1874   { LEVEL_FILE_TYPE_MM,         "MM"    },
1875   { LEVEL_FILE_TYPE_MM,         "DF"    },
1876   { -1,                         NULL    },
1877 };
1878
1879
1880 // ============================================================================
1881 // level file functions
1882 // ============================================================================
1883
1884 static boolean check_special_flags(char *flag)
1885 {
1886   if (strEqual(options.special_flags, flag) ||
1887       strEqual(leveldir_current->special_flags, flag))
1888     return TRUE;
1889
1890   return FALSE;
1891 }
1892
1893 static struct DateInfo getCurrentDate(void)
1894 {
1895   time_t epoch_seconds = time(NULL);
1896   struct tm *now = localtime(&epoch_seconds);
1897   struct DateInfo date;
1898
1899   date.year  = now->tm_year + 1900;
1900   date.month = now->tm_mon  + 1;
1901   date.day   = now->tm_mday;
1902
1903   date.src   = DATE_SRC_CLOCK;
1904
1905   return date;
1906 }
1907
1908 static void resetEventFlags(struct ElementChangeInfo *change)
1909 {
1910   int i;
1911
1912   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1913     change->has_event[i] = FALSE;
1914 }
1915
1916 static void resetEventBits(void)
1917 {
1918   int i;
1919
1920   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1921     xx_event_bits[i] = 0;
1922 }
1923
1924 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1925 {
1926   int i;
1927
1928   /* important: only change event flag if corresponding event bit is set
1929      (this is because all xx_event_bits[] values are loaded separately,
1930      and all xx_event_bits[] values are set back to zero before loading
1931      another value xx_event_bits[x] (each value representing 32 flags)) */
1932
1933   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1934     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1935       change->has_event[i] = TRUE;
1936 }
1937
1938 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1939 {
1940   int i;
1941
1942   /* in contrast to the above function setEventFlagsFromEventBits(), it
1943      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1944      depending on the corresponding change->has_event[i] values here, as
1945      all xx_event_bits[] values are reset in resetEventBits() before */
1946
1947   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1948     if (change->has_event[i])
1949       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1950 }
1951
1952 static char *getDefaultElementDescription(struct ElementInfo *ei)
1953 {
1954   static char description[MAX_ELEMENT_NAME_LEN + 1];
1955   char *default_description = (ei->custom_description != NULL ?
1956                                ei->custom_description :
1957                                ei->editor_description);
1958   int i;
1959
1960   // always start with reliable default values
1961   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1962     description[i] = '\0';
1963
1964   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1965   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1966
1967   return &description[0];
1968 }
1969
1970 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1971 {
1972   char *default_description = getDefaultElementDescription(ei);
1973   int i;
1974
1975   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1976     ei->description[i] = default_description[i];
1977 }
1978
1979 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
1980 {
1981   int i;
1982
1983   for (i = 0; conf[i].data_type != -1; i++)
1984   {
1985     int default_value = conf[i].default_value;
1986     int data_type = conf[i].data_type;
1987     int conf_type = conf[i].conf_type;
1988     int byte_mask = conf_type & CONF_MASK_BYTES;
1989
1990     if (byte_mask == CONF_MASK_MULTI_BYTES)
1991     {
1992       int default_num_entities = conf[i].default_num_entities;
1993       int max_num_entities = conf[i].max_num_entities;
1994
1995       *(int *)(conf[i].num_entities) = default_num_entities;
1996
1997       if (data_type == TYPE_STRING)
1998       {
1999         char *default_string = conf[i].default_string;
2000         char *string = (char *)(conf[i].value);
2001
2002         strncpy(string, default_string, max_num_entities);
2003       }
2004       else if (data_type == TYPE_ELEMENT_LIST)
2005       {
2006         int *element_array = (int *)(conf[i].value);
2007         int j;
2008
2009         for (j = 0; j < max_num_entities; j++)
2010           element_array[j] = default_value;
2011       }
2012       else if (data_type == TYPE_CONTENT_LIST)
2013       {
2014         struct Content *content = (struct Content *)(conf[i].value);
2015         int c, x, y;
2016
2017         for (c = 0; c < max_num_entities; c++)
2018           for (y = 0; y < 3; y++)
2019             for (x = 0; x < 3; x++)
2020               content[c].e[x][y] = default_value;
2021       }
2022     }
2023     else        // constant size configuration data (1, 2 or 4 bytes)
2024     {
2025       if (data_type == TYPE_BOOLEAN)
2026         *(boolean *)(conf[i].value) = default_value;
2027       else
2028         *(int *)    (conf[i].value) = default_value;
2029     }
2030   }
2031 }
2032
2033 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2034 {
2035   int i;
2036
2037   for (i = 0; conf[i].data_type != -1; i++)
2038   {
2039     int data_type = conf[i].data_type;
2040     int conf_type = conf[i].conf_type;
2041     int byte_mask = conf_type & CONF_MASK_BYTES;
2042
2043     if (byte_mask == CONF_MASK_MULTI_BYTES)
2044     {
2045       int max_num_entities = conf[i].max_num_entities;
2046
2047       if (data_type == TYPE_STRING)
2048       {
2049         char *string      = (char *)(conf[i].value);
2050         char *string_copy = (char *)(conf[i].value_copy);
2051
2052         strncpy(string_copy, string, max_num_entities);
2053       }
2054       else if (data_type == TYPE_ELEMENT_LIST)
2055       {
2056         int *element_array      = (int *)(conf[i].value);
2057         int *element_array_copy = (int *)(conf[i].value_copy);
2058         int j;
2059
2060         for (j = 0; j < max_num_entities; j++)
2061           element_array_copy[j] = element_array[j];
2062       }
2063       else if (data_type == TYPE_CONTENT_LIST)
2064       {
2065         struct Content *content      = (struct Content *)(conf[i].value);
2066         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2067         int c, x, y;
2068
2069         for (c = 0; c < max_num_entities; c++)
2070           for (y = 0; y < 3; y++)
2071             for (x = 0; x < 3; x++)
2072               content_copy[c].e[x][y] = content[c].e[x][y];
2073       }
2074     }
2075     else        // constant size configuration data (1, 2 or 4 bytes)
2076     {
2077       if (data_type == TYPE_BOOLEAN)
2078         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2079       else
2080         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2081     }
2082   }
2083 }
2084
2085 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2086 {
2087   int i;
2088
2089   xx_ei = *ei_from;     // copy element data into temporary buffer
2090   yy_ei = *ei_to;       // copy element data into temporary buffer
2091
2092   copyConfigFromConfigList(chunk_config_CUSX_base);
2093
2094   *ei_from = xx_ei;
2095   *ei_to   = yy_ei;
2096
2097   // ---------- reinitialize and copy change pages ----------
2098
2099   ei_to->num_change_pages = ei_from->num_change_pages;
2100   ei_to->current_change_page = ei_from->current_change_page;
2101
2102   setElementChangePages(ei_to, ei_to->num_change_pages);
2103
2104   for (i = 0; i < ei_to->num_change_pages; i++)
2105     ei_to->change_page[i] = ei_from->change_page[i];
2106
2107   // ---------- copy group element info ----------
2108   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2109     *ei_to->group = *ei_from->group;
2110
2111   // mark this custom element as modified
2112   ei_to->modified_settings = TRUE;
2113 }
2114
2115 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2116 {
2117   int change_page_size = sizeof(struct ElementChangeInfo);
2118
2119   ei->num_change_pages = MAX(1, change_pages);
2120
2121   ei->change_page =
2122     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2123
2124   if (ei->current_change_page >= ei->num_change_pages)
2125     ei->current_change_page = ei->num_change_pages - 1;
2126
2127   ei->change = &ei->change_page[ei->current_change_page];
2128 }
2129
2130 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2131 {
2132   xx_change = *change;          // copy change data into temporary buffer
2133
2134   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2135
2136   *change = xx_change;
2137
2138   resetEventFlags(change);
2139
2140   change->direct_action = 0;
2141   change->other_action = 0;
2142
2143   change->pre_change_function = NULL;
2144   change->change_function = NULL;
2145   change->post_change_function = NULL;
2146 }
2147
2148 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2149 {
2150   int i, x, y;
2151
2152   li = *level;          // copy level data into temporary buffer
2153   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2154   *level = li;          // copy temporary buffer back to level data
2155
2156   setLevelInfoToDefaults_BD();
2157   setLevelInfoToDefaults_EM();
2158   setLevelInfoToDefaults_SP();
2159   setLevelInfoToDefaults_MM();
2160
2161   level->native_bd_level = &native_bd_level;
2162   level->native_em_level = &native_em_level;
2163   level->native_sp_level = &native_sp_level;
2164   level->native_mm_level = &native_mm_level;
2165
2166   level->file_version = FILE_VERSION_ACTUAL;
2167   level->game_version = GAME_VERSION_ACTUAL;
2168
2169   level->creation_date = getCurrentDate();
2170
2171   level->encoding_16bit_field  = TRUE;
2172   level->encoding_16bit_yamyam = TRUE;
2173   level->encoding_16bit_amoeba = TRUE;
2174
2175   // clear level name and level author string buffers
2176   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2177     level->name[i] = '\0';
2178   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2179     level->author[i] = '\0';
2180
2181   // set level name and level author to default values
2182   strcpy(level->name, NAMELESS_LEVEL_NAME);
2183   strcpy(level->author, ANONYMOUS_NAME);
2184
2185   // set level playfield to playable default level with player and exit
2186   for (x = 0; x < MAX_LEV_FIELDX; x++)
2187     for (y = 0; y < MAX_LEV_FIELDY; y++)
2188       level->field[x][y] = EL_SAND;
2189
2190   level->field[0][0] = EL_PLAYER_1;
2191   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2192
2193   BorderElement = EL_STEELWALL;
2194
2195   // detect custom elements when loading them
2196   level->file_has_custom_elements = FALSE;
2197
2198   // set all bug compatibility flags to "false" => do not emulate this bug
2199   level->use_action_after_change_bug = FALSE;
2200
2201   if (leveldir_current)
2202   {
2203     // try to determine better author name than 'anonymous'
2204     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2205     {
2206       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2207       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2208     }
2209     else
2210     {
2211       switch (LEVELCLASS(leveldir_current))
2212       {
2213         case LEVELCLASS_TUTORIAL:
2214           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2215           break;
2216
2217         case LEVELCLASS_CONTRIB:
2218           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2219           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2220           break;
2221
2222         case LEVELCLASS_PRIVATE:
2223           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2224           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2225           break;
2226
2227         default:
2228           // keep default value
2229           break;
2230       }
2231     }
2232   }
2233 }
2234
2235 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2236 {
2237   static boolean clipboard_elements_initialized = FALSE;
2238   int i;
2239
2240   InitElementPropertiesStatic();
2241
2242   li = *level;          // copy level data into temporary buffer
2243   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2244   *level = li;          // copy temporary buffer back to level data
2245
2246   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2247   {
2248     int element = i;
2249     struct ElementInfo *ei = &element_info[element];
2250
2251     if (element == EL_MM_GRAY_BALL)
2252     {
2253       struct LevelInfo_MM *level_mm = level->native_mm_level;
2254       int j;
2255
2256       for (j = 0; j < level->num_mm_ball_contents; j++)
2257         level->mm_ball_content[j] =
2258           map_element_MM_to_RND(level_mm->ball_content[j]);
2259     }
2260
2261     // never initialize clipboard elements after the very first time
2262     // (to be able to use clipboard elements between several levels)
2263     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2264       continue;
2265
2266     if (IS_ENVELOPE(element))
2267     {
2268       int envelope_nr = element - EL_ENVELOPE_1;
2269
2270       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2271
2272       level->envelope[envelope_nr] = xx_envelope;
2273     }
2274
2275     if (IS_CUSTOM_ELEMENT(element) ||
2276         IS_GROUP_ELEMENT(element) ||
2277         IS_INTERNAL_ELEMENT(element))
2278     {
2279       xx_ei = *ei;      // copy element data into temporary buffer
2280
2281       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2282
2283       *ei = xx_ei;
2284     }
2285
2286     setElementChangePages(ei, 1);
2287     setElementChangeInfoToDefaults(ei->change);
2288
2289     if (IS_CUSTOM_ELEMENT(element) ||
2290         IS_GROUP_ELEMENT(element))
2291     {
2292       setElementDescriptionToDefault(ei);
2293
2294       ei->modified_settings = FALSE;
2295     }
2296
2297     if (IS_CUSTOM_ELEMENT(element) ||
2298         IS_INTERNAL_ELEMENT(element))
2299     {
2300       // internal values used in level editor
2301
2302       ei->access_type = 0;
2303       ei->access_layer = 0;
2304       ei->access_protected = 0;
2305       ei->walk_to_action = 0;
2306       ei->smash_targets = 0;
2307       ei->deadliness = 0;
2308
2309       ei->can_explode_by_fire = FALSE;
2310       ei->can_explode_smashed = FALSE;
2311       ei->can_explode_impact = FALSE;
2312
2313       ei->current_change_page = 0;
2314     }
2315
2316     if (IS_GROUP_ELEMENT(element) ||
2317         IS_INTERNAL_ELEMENT(element))
2318     {
2319       struct ElementGroupInfo *group;
2320
2321       // initialize memory for list of elements in group
2322       if (ei->group == NULL)
2323         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2324
2325       group = ei->group;
2326
2327       xx_group = *group;        // copy group data into temporary buffer
2328
2329       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2330
2331       *group = xx_group;
2332     }
2333
2334     if (IS_EMPTY_ELEMENT(element) ||
2335         IS_INTERNAL_ELEMENT(element))
2336     {
2337       xx_ei = *ei;              // copy element data into temporary buffer
2338
2339       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2340
2341       *ei = xx_ei;
2342     }
2343   }
2344
2345   clipboard_elements_initialized = TRUE;
2346 }
2347
2348 static void setLevelInfoToDefaults(struct LevelInfo *level,
2349                                    boolean level_info_only,
2350                                    boolean reset_file_status)
2351 {
2352   setLevelInfoToDefaults_Level(level);
2353
2354   if (!level_info_only)
2355     setLevelInfoToDefaults_Elements(level);
2356
2357   if (reset_file_status)
2358   {
2359     level->no_valid_file = FALSE;
2360     level->no_level_file = FALSE;
2361   }
2362
2363   level->changed = FALSE;
2364 }
2365
2366 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2367 {
2368   level_file_info->nr = 0;
2369   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2370   level_file_info->packed = FALSE;
2371
2372   setString(&level_file_info->basename, NULL);
2373   setString(&level_file_info->filename, NULL);
2374 }
2375
2376 int getMappedElement_SB(int, boolean);
2377
2378 static void ActivateLevelTemplate(void)
2379 {
2380   int x, y;
2381
2382   if (check_special_flags("load_xsb_to_ces"))
2383   {
2384     // fill smaller playfields with padding "beyond border wall" elements
2385     if (level.fieldx < level_template.fieldx ||
2386         level.fieldy < level_template.fieldy)
2387     {
2388       short field[level.fieldx][level.fieldy];
2389       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2390       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2391       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2392       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2393
2394       // copy old playfield (which is smaller than the visible area)
2395       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2396         field[x][y] = level.field[x][y];
2397
2398       // fill new, larger playfield with "beyond border wall" elements
2399       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2400         level.field[x][y] = getMappedElement_SB('_', TRUE);
2401
2402       // copy the old playfield to the middle of the new playfield
2403       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2404         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2405
2406       level.fieldx = new_fieldx;
2407       level.fieldy = new_fieldy;
2408     }
2409   }
2410
2411   // Currently there is no special action needed to activate the template
2412   // data, because 'element_info' property settings overwrite the original
2413   // level data, while all other variables do not change.
2414
2415   // Exception: 'from_level_template' elements in the original level playfield
2416   // are overwritten with the corresponding elements at the same position in
2417   // playfield from the level template.
2418
2419   for (x = 0; x < level.fieldx; x++)
2420     for (y = 0; y < level.fieldy; y++)
2421       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2422         level.field[x][y] = level_template.field[x][y];
2423
2424   if (check_special_flags("load_xsb_to_ces"))
2425   {
2426     struct LevelInfo level_backup = level;
2427
2428     // overwrite all individual level settings from template level settings
2429     level = level_template;
2430
2431     // restore level file info
2432     level.file_info = level_backup.file_info;
2433
2434     // restore playfield size
2435     level.fieldx = level_backup.fieldx;
2436     level.fieldy = level_backup.fieldy;
2437
2438     // restore playfield content
2439     for (x = 0; x < level.fieldx; x++)
2440       for (y = 0; y < level.fieldy; y++)
2441         level.field[x][y] = level_backup.field[x][y];
2442
2443     // restore name and author from individual level
2444     strcpy(level.name,   level_backup.name);
2445     strcpy(level.author, level_backup.author);
2446
2447     // restore flag "use_custom_template"
2448     level.use_custom_template = level_backup.use_custom_template;
2449   }
2450 }
2451
2452 static boolean checkForPackageFromBasename_BD(char *basename)
2453 {
2454   // check for native BD level file extensions
2455   if (!strSuffixLower(basename, ".bd") &&
2456       !strSuffixLower(basename, ".bdr") &&
2457       !strSuffixLower(basename, ".brc") &&
2458       !strSuffixLower(basename, ".gds"))
2459     return FALSE;
2460
2461   // check for standard single-level BD files (like "001.bd")
2462   if (strSuffixLower(basename, ".bd") &&
2463       strlen(basename) == 6 &&
2464       basename[0] >= '0' && basename[0] <= '9' &&
2465       basename[1] >= '0' && basename[1] <= '9' &&
2466       basename[2] >= '0' && basename[2] <= '9')
2467     return FALSE;
2468
2469   // this is a level package in native BD file format
2470   return TRUE;
2471 }
2472
2473 static char *getLevelFilenameFromBasename(char *basename)
2474 {
2475   static char *filename = NULL;
2476
2477   checked_free(filename);
2478
2479   filename = getPath2(getCurrentLevelDir(), basename);
2480
2481   return filename;
2482 }
2483
2484 static int getFileTypeFromBasename(char *basename)
2485 {
2486   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2487
2488   static char *filename = NULL;
2489   struct stat file_status;
2490
2491   // ---------- try to determine file type from filename ----------
2492
2493   // check for typical filename of a Supaplex level package file
2494   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2495     return LEVEL_FILE_TYPE_SP;
2496
2497   // check for typical filename of a Diamond Caves II level package file
2498   if (strSuffixLower(basename, ".dc") ||
2499       strSuffixLower(basename, ".dc2"))
2500     return LEVEL_FILE_TYPE_DC;
2501
2502   // check for typical filename of a Sokoban level package file
2503   if (strSuffixLower(basename, ".xsb") &&
2504       strchr(basename, '%') == NULL)
2505     return LEVEL_FILE_TYPE_SB;
2506
2507   // check for typical filename of a Boulder Dash (GDash) level package file
2508   if (checkForPackageFromBasename_BD(basename))
2509     return LEVEL_FILE_TYPE_BD;
2510
2511   // ---------- try to determine file type from filesize ----------
2512
2513   checked_free(filename);
2514   filename = getPath2(getCurrentLevelDir(), basename);
2515
2516   if (stat(filename, &file_status) == 0)
2517   {
2518     // check for typical filesize of a Supaplex level package file
2519     if (file_status.st_size == 170496)
2520       return LEVEL_FILE_TYPE_SP;
2521   }
2522
2523   return LEVEL_FILE_TYPE_UNKNOWN;
2524 }
2525
2526 static int getFileTypeFromMagicBytes(char *filename, int type)
2527 {
2528   File *file;
2529
2530   if ((file = openFile(filename, MODE_READ)))
2531   {
2532     char chunk_name[CHUNK_ID_LEN + 1];
2533
2534     getFileChunkBE(file, chunk_name, NULL);
2535
2536     if (strEqual(chunk_name, "MMII") ||
2537         strEqual(chunk_name, "MIRR"))
2538       type = LEVEL_FILE_TYPE_MM;
2539
2540     closeFile(file);
2541   }
2542
2543   return type;
2544 }
2545
2546 static boolean checkForPackageFromBasename(char *basename)
2547 {
2548   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2549   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2550
2551   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2552 }
2553
2554 static char *getSingleLevelBasenameExt(int nr, char *extension)
2555 {
2556   static char basename[MAX_FILENAME_LEN];
2557
2558   if (nr < 0)
2559     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2560   else
2561     sprintf(basename, "%03d.%s", nr, extension);
2562
2563   return basename;
2564 }
2565
2566 static char *getSingleLevelBasename(int nr)
2567 {
2568   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2569 }
2570
2571 static char *getPackedLevelBasename(int type)
2572 {
2573   static char basename[MAX_FILENAME_LEN];
2574   char *directory = getCurrentLevelDir();
2575   Directory *dir;
2576   DirectoryEntry *dir_entry;
2577
2578   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2579
2580   if ((dir = openDirectory(directory)) == NULL)
2581   {
2582     Warn("cannot read current level directory '%s'", directory);
2583
2584     return basename;
2585   }
2586
2587   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2588   {
2589     char *entry_basename = dir_entry->basename;
2590     int entry_type = getFileTypeFromBasename(entry_basename);
2591
2592     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2593     {
2594       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2595           type == entry_type)
2596       {
2597         strcpy(basename, entry_basename);
2598
2599         break;
2600       }
2601     }
2602   }
2603
2604   closeDirectory(dir);
2605
2606   return basename;
2607 }
2608
2609 static char *getSingleLevelFilename(int nr)
2610 {
2611   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2612 }
2613
2614 #if ENABLE_UNUSED_CODE
2615 static char *getPackedLevelFilename(int type)
2616 {
2617   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2618 }
2619 #endif
2620
2621 char *getDefaultLevelFilename(int nr)
2622 {
2623   return getSingleLevelFilename(nr);
2624 }
2625
2626 #if ENABLE_UNUSED_CODE
2627 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2628                                                  int type)
2629 {
2630   lfi->type = type;
2631   lfi->packed = FALSE;
2632
2633   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2634   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2635 }
2636 #endif
2637
2638 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2639                                                  int type, char *format, ...)
2640 {
2641   static char basename[MAX_FILENAME_LEN];
2642   va_list ap;
2643
2644   va_start(ap, format);
2645   vsprintf(basename, format, ap);
2646   va_end(ap);
2647
2648   lfi->type = type;
2649   lfi->packed = FALSE;
2650
2651   setString(&lfi->basename, basename);
2652   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2653 }
2654
2655 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2656                                                  int type)
2657 {
2658   lfi->type = type;
2659   lfi->packed = TRUE;
2660
2661   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2662   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2663 }
2664
2665 static int getFiletypeFromID(char *filetype_id)
2666 {
2667   char *filetype_id_lower;
2668   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2669   int i;
2670
2671   if (filetype_id == NULL)
2672     return LEVEL_FILE_TYPE_UNKNOWN;
2673
2674   filetype_id_lower = getStringToLower(filetype_id);
2675
2676   for (i = 0; filetype_id_list[i].id != NULL; i++)
2677   {
2678     char *id_lower = getStringToLower(filetype_id_list[i].id);
2679     
2680     if (strEqual(filetype_id_lower, id_lower))
2681       filetype = filetype_id_list[i].filetype;
2682
2683     free(id_lower);
2684
2685     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2686       break;
2687   }
2688
2689   free(filetype_id_lower);
2690
2691   return filetype;
2692 }
2693
2694 char *getLocalLevelTemplateFilename(void)
2695 {
2696   return getDefaultLevelFilename(-1);
2697 }
2698
2699 char *getGlobalLevelTemplateFilename(void)
2700 {
2701   // global variable "leveldir_current" must be modified in the loop below
2702   LevelDirTree *leveldir_current_last = leveldir_current;
2703   char *filename = NULL;
2704
2705   // check for template level in path from current to topmost tree node
2706
2707   while (leveldir_current != NULL)
2708   {
2709     filename = getDefaultLevelFilename(-1);
2710
2711     if (fileExists(filename))
2712       break;
2713
2714     leveldir_current = leveldir_current->node_parent;
2715   }
2716
2717   // restore global variable "leveldir_current" modified in above loop
2718   leveldir_current = leveldir_current_last;
2719
2720   return filename;
2721 }
2722
2723 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2724 {
2725   int nr = lfi->nr;
2726
2727   // special case: level number is negative => check for level template file
2728   if (nr < 0)
2729   {
2730     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2731                                          getSingleLevelBasename(-1));
2732
2733     // replace local level template filename with global template filename
2734     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2735
2736     // no fallback if template file not existing
2737     return;
2738   }
2739
2740   // special case: check for file name/pattern specified in "levelinfo.conf"
2741   if (leveldir_current->level_filename != NULL)
2742   {
2743     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2744
2745     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2746                                          leveldir_current->level_filename, nr);
2747
2748     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2749
2750     if (fileExists(lfi->filename))
2751       return;
2752   }
2753   else if (leveldir_current->level_filetype != NULL)
2754   {
2755     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2756
2757     // check for specified native level file with standard file name
2758     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2759                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2760     if (fileExists(lfi->filename))
2761       return;
2762   }
2763
2764   // check for native Rocks'n'Diamonds level file
2765   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2766                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2767   if (fileExists(lfi->filename))
2768     return;
2769
2770   // check for native Boulder Dash level file
2771   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2772   if (fileExists(lfi->filename))
2773     return;
2774
2775   // check for Emerald Mine level file (V1)
2776   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2777                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2778   if (fileExists(lfi->filename))
2779     return;
2780   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2781                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2782   if (fileExists(lfi->filename))
2783     return;
2784
2785   // check for Emerald Mine level file (V2 to V5)
2786   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2787   if (fileExists(lfi->filename))
2788     return;
2789
2790   // check for Emerald Mine level file (V6 / single mode)
2791   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2792   if (fileExists(lfi->filename))
2793     return;
2794   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2795   if (fileExists(lfi->filename))
2796     return;
2797
2798   // check for Emerald Mine level file (V6 / teamwork mode)
2799   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2800   if (fileExists(lfi->filename))
2801     return;
2802   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2803   if (fileExists(lfi->filename))
2804     return;
2805
2806   // check for various packed level file formats
2807   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2808   if (fileExists(lfi->filename))
2809     return;
2810
2811   // no known level file found -- use default values (and fail later)
2812   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2813                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2814 }
2815
2816 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2817 {
2818   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2819     lfi->type = getFileTypeFromBasename(lfi->basename);
2820
2821   if (lfi->type == LEVEL_FILE_TYPE_RND)
2822     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2823 }
2824
2825 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2826 {
2827   // always start with reliable default values
2828   setFileInfoToDefaults(level_file_info);
2829
2830   level_file_info->nr = nr;     // set requested level number
2831
2832   determineLevelFileInfo_Filename(level_file_info);
2833   determineLevelFileInfo_Filetype(level_file_info);
2834 }
2835
2836 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2837                               struct LevelFileInfo *lfi_to)
2838 {
2839   lfi_to->nr     = lfi_from->nr;
2840   lfi_to->type   = lfi_from->type;
2841   lfi_to->packed = lfi_from->packed;
2842
2843   setString(&lfi_to->basename, lfi_from->basename);
2844   setString(&lfi_to->filename, lfi_from->filename);
2845 }
2846
2847 // ----------------------------------------------------------------------------
2848 // functions for loading R'n'D level
2849 // ----------------------------------------------------------------------------
2850
2851 int getMappedElement(int element)
2852 {
2853   // remap some (historic, now obsolete) elements
2854
2855   switch (element)
2856   {
2857     case EL_PLAYER_OBSOLETE:
2858       element = EL_PLAYER_1;
2859       break;
2860
2861     case EL_KEY_OBSOLETE:
2862       element = EL_KEY_1;
2863       break;
2864
2865     case EL_EM_KEY_1_FILE_OBSOLETE:
2866       element = EL_EM_KEY_1;
2867       break;
2868
2869     case EL_EM_KEY_2_FILE_OBSOLETE:
2870       element = EL_EM_KEY_2;
2871       break;
2872
2873     case EL_EM_KEY_3_FILE_OBSOLETE:
2874       element = EL_EM_KEY_3;
2875       break;
2876
2877     case EL_EM_KEY_4_FILE_OBSOLETE:
2878       element = EL_EM_KEY_4;
2879       break;
2880
2881     case EL_ENVELOPE_OBSOLETE:
2882       element = EL_ENVELOPE_1;
2883       break;
2884
2885     case EL_SP_EMPTY:
2886       element = EL_EMPTY;
2887       break;
2888
2889     default:
2890       if (element >= NUM_FILE_ELEMENTS)
2891       {
2892         Warn("invalid level element %d", element);
2893
2894         element = EL_UNKNOWN;
2895       }
2896       break;
2897   }
2898
2899   return element;
2900 }
2901
2902 static int getMappedElementByVersion(int element, int game_version)
2903 {
2904   // remap some elements due to certain game version
2905
2906   if (game_version <= VERSION_IDENT(2,2,0,0))
2907   {
2908     // map game font elements
2909     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2910                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2911                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2912                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2913   }
2914
2915   if (game_version < VERSION_IDENT(3,0,0,0))
2916   {
2917     // map Supaplex gravity tube elements
2918     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2919                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2920                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2921                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2922                element);
2923   }
2924
2925   return element;
2926 }
2927
2928 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2929 {
2930   level->file_version = getFileVersion(file);
2931   level->game_version = getFileVersion(file);
2932
2933   return chunk_size;
2934 }
2935
2936 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2937 {
2938   level->creation_date.year  = getFile16BitBE(file);
2939   level->creation_date.month = getFile8Bit(file);
2940   level->creation_date.day   = getFile8Bit(file);
2941
2942   level->creation_date.src   = DATE_SRC_LEVELFILE;
2943
2944   return chunk_size;
2945 }
2946
2947 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2948 {
2949   int initial_player_stepsize;
2950   int initial_player_gravity;
2951   int i, x, y;
2952
2953   level->fieldx = getFile8Bit(file);
2954   level->fieldy = getFile8Bit(file);
2955
2956   level->time           = getFile16BitBE(file);
2957   level->gems_needed    = getFile16BitBE(file);
2958
2959   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2960     level->name[i] = getFile8Bit(file);
2961   level->name[MAX_LEVEL_NAME_LEN] = 0;
2962
2963   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2964     level->score[i] = getFile8Bit(file);
2965
2966   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2967   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2968     for (y = 0; y < 3; y++)
2969       for (x = 0; x < 3; x++)
2970         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2971
2972   level->amoeba_speed           = getFile8Bit(file);
2973   level->time_magic_wall        = getFile8Bit(file);
2974   level->time_wheel             = getFile8Bit(file);
2975   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2976
2977   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
2978                                    STEPSIZE_NORMAL);
2979
2980   for (i = 0; i < MAX_PLAYERS; i++)
2981     level->initial_player_stepsize[i] = initial_player_stepsize;
2982
2983   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2984
2985   for (i = 0; i < MAX_PLAYERS; i++)
2986     level->initial_player_gravity[i] = initial_player_gravity;
2987
2988   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2989   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2990
2991   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2992
2993   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2994   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2995   level->can_move_into_acid_bits = getFile32BitBE(file);
2996   level->dont_collide_with_bits = getFile8Bit(file);
2997
2998   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
2999   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3000
3001   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3002   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3003   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3004
3005   level->game_engine_type       = getFile8Bit(file);
3006
3007   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3008
3009   return chunk_size;
3010 }
3011
3012 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3013 {
3014   int i;
3015
3016   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3017     level->name[i] = getFile8Bit(file);
3018   level->name[MAX_LEVEL_NAME_LEN] = 0;
3019
3020   return chunk_size;
3021 }
3022
3023 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3024 {
3025   int i;
3026
3027   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3028     level->author[i] = getFile8Bit(file);
3029   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3030
3031   return chunk_size;
3032 }
3033
3034 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3035 {
3036   int x, y;
3037   int chunk_size_expected = level->fieldx * level->fieldy;
3038
3039   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3040      stored with 16-bit encoding (and should be twice as big then).
3041      Even worse, playfield data was stored 16-bit when only yamyam content
3042      contained 16-bit elements and vice versa. */
3043
3044   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3045     chunk_size_expected *= 2;
3046
3047   if (chunk_size_expected != chunk_size)
3048   {
3049     ReadUnusedBytesFromFile(file, chunk_size);
3050     return chunk_size_expected;
3051   }
3052
3053   for (y = 0; y < level->fieldy; y++)
3054     for (x = 0; x < level->fieldx; x++)
3055       level->field[x][y] =
3056         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3057                          getFile8Bit(file));
3058   return chunk_size;
3059 }
3060
3061 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3062 {
3063   int i, x, y;
3064   int header_size = 4;
3065   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3066   int chunk_size_expected = header_size + content_size;
3067
3068   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3069      stored with 16-bit encoding (and should be twice as big then).
3070      Even worse, playfield data was stored 16-bit when only yamyam content
3071      contained 16-bit elements and vice versa. */
3072
3073   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3074     chunk_size_expected += content_size;
3075
3076   if (chunk_size_expected != chunk_size)
3077   {
3078     ReadUnusedBytesFromFile(file, chunk_size);
3079     return chunk_size_expected;
3080   }
3081
3082   getFile8Bit(file);
3083   level->num_yamyam_contents = getFile8Bit(file);
3084   getFile8Bit(file);
3085   getFile8Bit(file);
3086
3087   // correct invalid number of content fields -- should never happen
3088   if (level->num_yamyam_contents < 1 ||
3089       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3090     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3091
3092   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3093     for (y = 0; y < 3; y++)
3094       for (x = 0; x < 3; x++)
3095         level->yamyam_content[i].e[x][y] =
3096           getMappedElement(level->encoding_16bit_field ?
3097                            getFile16BitBE(file) : getFile8Bit(file));
3098   return chunk_size;
3099 }
3100
3101 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3102 {
3103   int i, x, y;
3104   int element;
3105   int num_contents;
3106   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3107
3108   element = getMappedElement(getFile16BitBE(file));
3109   num_contents = getFile8Bit(file);
3110
3111   getFile8Bit(file);    // content x size (unused)
3112   getFile8Bit(file);    // content y size (unused)
3113
3114   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3115
3116   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3117     for (y = 0; y < 3; y++)
3118       for (x = 0; x < 3; x++)
3119         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3120
3121   // correct invalid number of content fields -- should never happen
3122   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3123     num_contents = STD_ELEMENT_CONTENTS;
3124
3125   if (element == EL_YAMYAM)
3126   {
3127     level->num_yamyam_contents = num_contents;
3128
3129     for (i = 0; i < num_contents; i++)
3130       for (y = 0; y < 3; y++)
3131         for (x = 0; x < 3; x++)
3132           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3133   }
3134   else if (element == EL_BD_AMOEBA)
3135   {
3136     level->amoeba_content = content_array[0][0][0];
3137   }
3138   else
3139   {
3140     Warn("cannot load content for element '%d'", element);
3141   }
3142
3143   return chunk_size;
3144 }
3145
3146 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3147 {
3148   int i;
3149   int element;
3150   int envelope_nr;
3151   int envelope_len;
3152   int chunk_size_expected;
3153
3154   element = getMappedElement(getFile16BitBE(file));
3155   if (!IS_ENVELOPE(element))
3156     element = EL_ENVELOPE_1;
3157
3158   envelope_nr = element - EL_ENVELOPE_1;
3159
3160   envelope_len = getFile16BitBE(file);
3161
3162   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3163   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3164
3165   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3166
3167   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3168   if (chunk_size_expected != chunk_size)
3169   {
3170     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3171     return chunk_size_expected;
3172   }
3173
3174   for (i = 0; i < envelope_len; i++)
3175     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3176
3177   return chunk_size;
3178 }
3179
3180 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3181 {
3182   int num_changed_custom_elements = getFile16BitBE(file);
3183   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3184   int i;
3185
3186   if (chunk_size_expected != chunk_size)
3187   {
3188     ReadUnusedBytesFromFile(file, chunk_size - 2);
3189     return chunk_size_expected;
3190   }
3191
3192   for (i = 0; i < num_changed_custom_elements; i++)
3193   {
3194     int element = getMappedElement(getFile16BitBE(file));
3195     int properties = getFile32BitBE(file);
3196
3197     if (IS_CUSTOM_ELEMENT(element))
3198       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3199     else
3200       Warn("invalid custom element number %d", element);
3201
3202     // older game versions that wrote level files with CUS1 chunks used
3203     // different default push delay values (not yet stored in level file)
3204     element_info[element].push_delay_fixed = 2;
3205     element_info[element].push_delay_random = 8;
3206   }
3207
3208   level->file_has_custom_elements = TRUE;
3209
3210   return chunk_size;
3211 }
3212
3213 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3214 {
3215   int num_changed_custom_elements = getFile16BitBE(file);
3216   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3217   int i;
3218
3219   if (chunk_size_expected != chunk_size)
3220   {
3221     ReadUnusedBytesFromFile(file, chunk_size - 2);
3222     return chunk_size_expected;
3223   }
3224
3225   for (i = 0; i < num_changed_custom_elements; i++)
3226   {
3227     int element = getMappedElement(getFile16BitBE(file));
3228     int custom_target_element = getMappedElement(getFile16BitBE(file));
3229
3230     if (IS_CUSTOM_ELEMENT(element))
3231       element_info[element].change->target_element = custom_target_element;
3232     else
3233       Warn("invalid custom element number %d", element);
3234   }
3235
3236   level->file_has_custom_elements = TRUE;
3237
3238   return chunk_size;
3239 }
3240
3241 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3242 {
3243   int num_changed_custom_elements = getFile16BitBE(file);
3244   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3245   int i, j, x, y;
3246
3247   if (chunk_size_expected != chunk_size)
3248   {
3249     ReadUnusedBytesFromFile(file, chunk_size - 2);
3250     return chunk_size_expected;
3251   }
3252
3253   for (i = 0; i < num_changed_custom_elements; i++)
3254   {
3255     int element = getMappedElement(getFile16BitBE(file));
3256     struct ElementInfo *ei = &element_info[element];
3257     unsigned int event_bits;
3258
3259     if (!IS_CUSTOM_ELEMENT(element))
3260     {
3261       Warn("invalid custom element number %d", element);
3262
3263       element = EL_INTERNAL_DUMMY;
3264     }
3265
3266     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3267       ei->description[j] = getFile8Bit(file);
3268     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3269
3270     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3271
3272     // some free bytes for future properties and padding
3273     ReadUnusedBytesFromFile(file, 7);
3274
3275     ei->use_gfx_element = getFile8Bit(file);
3276     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3277
3278     ei->collect_score_initial = getFile8Bit(file);
3279     ei->collect_count_initial = getFile8Bit(file);
3280
3281     ei->push_delay_fixed = getFile16BitBE(file);
3282     ei->push_delay_random = getFile16BitBE(file);
3283     ei->move_delay_fixed = getFile16BitBE(file);
3284     ei->move_delay_random = getFile16BitBE(file);
3285
3286     ei->move_pattern = getFile16BitBE(file);
3287     ei->move_direction_initial = getFile8Bit(file);
3288     ei->move_stepsize = getFile8Bit(file);
3289
3290     for (y = 0; y < 3; y++)
3291       for (x = 0; x < 3; x++)
3292         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3293
3294     // bits 0 - 31 of "has_event[]"
3295     event_bits = getFile32BitBE(file);
3296     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3297       if (event_bits & (1u << j))
3298         ei->change->has_event[j] = TRUE;
3299
3300     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3301
3302     ei->change->delay_fixed = getFile16BitBE(file);
3303     ei->change->delay_random = getFile16BitBE(file);
3304     ei->change->delay_frames = getFile16BitBE(file);
3305
3306     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3307
3308     ei->change->explode = getFile8Bit(file);
3309     ei->change->use_target_content = getFile8Bit(file);
3310     ei->change->only_if_complete = getFile8Bit(file);
3311     ei->change->use_random_replace = getFile8Bit(file);
3312
3313     ei->change->random_percentage = getFile8Bit(file);
3314     ei->change->replace_when = getFile8Bit(file);
3315
3316     for (y = 0; y < 3; y++)
3317       for (x = 0; x < 3; x++)
3318         ei->change->target_content.e[x][y] =
3319           getMappedElement(getFile16BitBE(file));
3320
3321     ei->slippery_type = getFile8Bit(file);
3322
3323     // some free bytes for future properties and padding
3324     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3325
3326     // mark that this custom element has been modified
3327     ei->modified_settings = TRUE;
3328   }
3329
3330   level->file_has_custom_elements = TRUE;
3331
3332   return chunk_size;
3333 }
3334
3335 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3336 {
3337   struct ElementInfo *ei;
3338   int chunk_size_expected;
3339   int element;
3340   int i, j, x, y;
3341
3342   // ---------- custom element base property values (96 bytes) ----------------
3343
3344   element = getMappedElement(getFile16BitBE(file));
3345
3346   if (!IS_CUSTOM_ELEMENT(element))
3347   {
3348     Warn("invalid custom element number %d", element);
3349
3350     ReadUnusedBytesFromFile(file, chunk_size - 2);
3351
3352     return chunk_size;
3353   }
3354
3355   ei = &element_info[element];
3356
3357   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3358     ei->description[i] = getFile8Bit(file);
3359   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3360
3361   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3362
3363   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3364
3365   ei->num_change_pages = getFile8Bit(file);
3366
3367   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3368   if (chunk_size_expected != chunk_size)
3369   {
3370     ReadUnusedBytesFromFile(file, chunk_size - 43);
3371     return chunk_size_expected;
3372   }
3373
3374   ei->ce_value_fixed_initial = getFile16BitBE(file);
3375   ei->ce_value_random_initial = getFile16BitBE(file);
3376   ei->use_last_ce_value = getFile8Bit(file);
3377
3378   ei->use_gfx_element = getFile8Bit(file);
3379   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3380
3381   ei->collect_score_initial = getFile8Bit(file);
3382   ei->collect_count_initial = getFile8Bit(file);
3383
3384   ei->drop_delay_fixed = getFile8Bit(file);
3385   ei->push_delay_fixed = getFile8Bit(file);
3386   ei->drop_delay_random = getFile8Bit(file);
3387   ei->push_delay_random = getFile8Bit(file);
3388   ei->move_delay_fixed = getFile16BitBE(file);
3389   ei->move_delay_random = getFile16BitBE(file);
3390
3391   // bits 0 - 15 of "move_pattern" ...
3392   ei->move_pattern = getFile16BitBE(file);
3393   ei->move_direction_initial = getFile8Bit(file);
3394   ei->move_stepsize = getFile8Bit(file);
3395
3396   ei->slippery_type = getFile8Bit(file);
3397
3398   for (y = 0; y < 3; y++)
3399     for (x = 0; x < 3; x++)
3400       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3401
3402   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3403   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3404   ei->move_leave_type = getFile8Bit(file);
3405
3406   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3407   ei->move_pattern |= (getFile16BitBE(file) << 16);
3408
3409   ei->access_direction = getFile8Bit(file);
3410
3411   ei->explosion_delay = getFile8Bit(file);
3412   ei->ignition_delay = getFile8Bit(file);
3413   ei->explosion_type = getFile8Bit(file);
3414
3415   // some free bytes for future custom property values and padding
3416   ReadUnusedBytesFromFile(file, 1);
3417
3418   // ---------- change page property values (48 bytes) ------------------------
3419
3420   setElementChangePages(ei, ei->num_change_pages);
3421
3422   for (i = 0; i < ei->num_change_pages; i++)
3423   {
3424     struct ElementChangeInfo *change = &ei->change_page[i];
3425     unsigned int event_bits;
3426
3427     // always start with reliable default values
3428     setElementChangeInfoToDefaults(change);
3429
3430     // bits 0 - 31 of "has_event[]" ...
3431     event_bits = getFile32BitBE(file);
3432     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3433       if (event_bits & (1u << j))
3434         change->has_event[j] = TRUE;
3435
3436     change->target_element = getMappedElement(getFile16BitBE(file));
3437
3438     change->delay_fixed = getFile16BitBE(file);
3439     change->delay_random = getFile16BitBE(file);
3440     change->delay_frames = getFile16BitBE(file);
3441
3442     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3443
3444     change->explode = getFile8Bit(file);
3445     change->use_target_content = getFile8Bit(file);
3446     change->only_if_complete = getFile8Bit(file);
3447     change->use_random_replace = getFile8Bit(file);
3448
3449     change->random_percentage = getFile8Bit(file);
3450     change->replace_when = getFile8Bit(file);
3451
3452     for (y = 0; y < 3; y++)
3453       for (x = 0; x < 3; x++)
3454         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3455
3456     change->can_change = getFile8Bit(file);
3457
3458     change->trigger_side = getFile8Bit(file);
3459
3460     change->trigger_player = getFile8Bit(file);
3461     change->trigger_page = getFile8Bit(file);
3462
3463     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3464                             CH_PAGE_ANY : (1 << change->trigger_page));
3465
3466     change->has_action = getFile8Bit(file);
3467     change->action_type = getFile8Bit(file);
3468     change->action_mode = getFile8Bit(file);
3469     change->action_arg = getFile16BitBE(file);
3470
3471     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3472     event_bits = getFile8Bit(file);
3473     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3474       if (event_bits & (1u << (j - 32)))
3475         change->has_event[j] = TRUE;
3476   }
3477
3478   // mark this custom element as modified
3479   ei->modified_settings = TRUE;
3480
3481   level->file_has_custom_elements = TRUE;
3482
3483   return chunk_size;
3484 }
3485
3486 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3487 {
3488   struct ElementInfo *ei;
3489   struct ElementGroupInfo *group;
3490   int element;
3491   int i;
3492
3493   element = getMappedElement(getFile16BitBE(file));
3494
3495   if (!IS_GROUP_ELEMENT(element))
3496   {
3497     Warn("invalid group element number %d", element);
3498
3499     ReadUnusedBytesFromFile(file, chunk_size - 2);
3500
3501     return chunk_size;
3502   }
3503
3504   ei = &element_info[element];
3505
3506   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3507     ei->description[i] = getFile8Bit(file);
3508   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3509
3510   group = element_info[element].group;
3511
3512   group->num_elements = getFile8Bit(file);
3513
3514   ei->use_gfx_element = getFile8Bit(file);
3515   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3516
3517   group->choice_mode = getFile8Bit(file);
3518
3519   // some free bytes for future values and padding
3520   ReadUnusedBytesFromFile(file, 3);
3521
3522   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3523     group->element[i] = getMappedElement(getFile16BitBE(file));
3524
3525   // mark this group element as modified
3526   element_info[element].modified_settings = TRUE;
3527
3528   level->file_has_custom_elements = TRUE;
3529
3530   return chunk_size;
3531 }
3532
3533 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3534                                 int element, int real_element)
3535 {
3536   int micro_chunk_size = 0;
3537   int conf_type = getFile8Bit(file);
3538   int byte_mask = conf_type & CONF_MASK_BYTES;
3539   boolean element_found = FALSE;
3540   int i;
3541
3542   micro_chunk_size += 1;
3543
3544   if (byte_mask == CONF_MASK_MULTI_BYTES)
3545   {
3546     int num_bytes = getFile16BitBE(file);
3547     byte *buffer = checked_malloc(num_bytes);
3548
3549     ReadBytesFromFile(file, buffer, num_bytes);
3550
3551     for (i = 0; conf[i].data_type != -1; i++)
3552     {
3553       if (conf[i].element == element &&
3554           conf[i].conf_type == conf_type)
3555       {
3556         int data_type = conf[i].data_type;
3557         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3558         int max_num_entities = conf[i].max_num_entities;
3559
3560         if (num_entities > max_num_entities)
3561         {
3562           Warn("truncating number of entities for element %d from %d to %d",
3563                element, num_entities, max_num_entities);
3564
3565           num_entities = max_num_entities;
3566         }
3567
3568         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3569                                   data_type == TYPE_CONTENT_LIST))
3570         {
3571           // for element and content lists, zero entities are not allowed
3572           Warn("found empty list of entities for element %d", element);
3573
3574           // do not set "num_entities" here to prevent reading behind buffer
3575
3576           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3577         }
3578         else
3579         {
3580           *(int *)(conf[i].num_entities) = num_entities;
3581         }
3582
3583         element_found = TRUE;
3584
3585         if (data_type == TYPE_STRING)
3586         {
3587           char *string = (char *)(conf[i].value);
3588           int j;
3589
3590           for (j = 0; j < max_num_entities; j++)
3591             string[j] = (j < num_entities ? buffer[j] : '\0');
3592         }
3593         else if (data_type == TYPE_ELEMENT_LIST)
3594         {
3595           int *element_array = (int *)(conf[i].value);
3596           int j;
3597
3598           for (j = 0; j < num_entities; j++)
3599             element_array[j] =
3600               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3601         }
3602         else if (data_type == TYPE_CONTENT_LIST)
3603         {
3604           struct Content *content= (struct Content *)(conf[i].value);
3605           int c, x, y;
3606
3607           for (c = 0; c < num_entities; c++)
3608             for (y = 0; y < 3; y++)
3609               for (x = 0; x < 3; x++)
3610                 content[c].e[x][y] =
3611                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3612         }
3613         else
3614           element_found = FALSE;
3615
3616         break;
3617       }
3618     }
3619
3620     checked_free(buffer);
3621
3622     micro_chunk_size += 2 + num_bytes;
3623   }
3624   else          // constant size configuration data (1, 2 or 4 bytes)
3625   {
3626     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3627                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3628                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3629
3630     for (i = 0; conf[i].data_type != -1; i++)
3631     {
3632       if (conf[i].element == element &&
3633           conf[i].conf_type == conf_type)
3634       {
3635         int data_type = conf[i].data_type;
3636
3637         if (data_type == TYPE_ELEMENT)
3638           value = getMappedElement(value);
3639
3640         if (data_type == TYPE_BOOLEAN)
3641           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3642         else
3643           *(int *)    (conf[i].value) = value;
3644
3645         element_found = TRUE;
3646
3647         break;
3648       }
3649     }
3650
3651     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3652   }
3653
3654   if (!element_found)
3655   {
3656     char *error_conf_chunk_bytes =
3657       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3658        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3659        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3660     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3661     int error_element = real_element;
3662
3663     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3664          error_conf_chunk_bytes, error_conf_chunk_token,
3665          error_element, EL_NAME(error_element));
3666   }
3667
3668   return micro_chunk_size;
3669 }
3670
3671 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3672 {
3673   int real_chunk_size = 0;
3674
3675   li = *level;          // copy level data into temporary buffer
3676
3677   while (!checkEndOfFile(file))
3678   {
3679     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3680
3681     if (real_chunk_size >= chunk_size)
3682       break;
3683   }
3684
3685   *level = li;          // copy temporary buffer back to level data
3686
3687   return real_chunk_size;
3688 }
3689
3690 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3691 {
3692   int real_chunk_size = 0;
3693
3694   li = *level;          // copy level data into temporary buffer
3695
3696   while (!checkEndOfFile(file))
3697   {
3698     int element = getMappedElement(getFile16BitBE(file));
3699
3700     real_chunk_size += 2;
3701     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3702                                             element, element);
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_ELEM(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_ELEM,
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_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3735 {
3736   int element = getMappedElement(getFile16BitBE(file));
3737   int envelope_nr = element - EL_ENVELOPE_1;
3738   int real_chunk_size = 2;
3739
3740   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3741
3742   while (!checkEndOfFile(file))
3743   {
3744     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3745                                             -1, element);
3746
3747     if (real_chunk_size >= chunk_size)
3748       break;
3749   }
3750
3751   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3752
3753   return real_chunk_size;
3754 }
3755
3756 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3757 {
3758   int element = getMappedElement(getFile16BitBE(file));
3759   int real_chunk_size = 2;
3760   struct ElementInfo *ei = &element_info[element];
3761   int i;
3762
3763   xx_ei = *ei;          // copy element data into temporary buffer
3764
3765   xx_ei.num_change_pages = -1;
3766
3767   while (!checkEndOfFile(file))
3768   {
3769     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3770                                             -1, element);
3771     if (xx_ei.num_change_pages != -1)
3772       break;
3773
3774     if (real_chunk_size >= chunk_size)
3775       break;
3776   }
3777
3778   *ei = xx_ei;
3779
3780   if (ei->num_change_pages == -1)
3781   {
3782     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3783          EL_NAME(element));
3784
3785     ei->num_change_pages = 1;
3786
3787     setElementChangePages(ei, 1);
3788     setElementChangeInfoToDefaults(ei->change);
3789
3790     return real_chunk_size;
3791   }
3792
3793   // initialize number of change pages stored for this custom element
3794   setElementChangePages(ei, ei->num_change_pages);
3795   for (i = 0; i < ei->num_change_pages; i++)
3796     setElementChangeInfoToDefaults(&ei->change_page[i]);
3797
3798   // start with reading properties for the first change page
3799   xx_current_change_page = 0;
3800
3801   while (!checkEndOfFile(file))
3802   {
3803     // level file might contain invalid change page number
3804     if (xx_current_change_page >= ei->num_change_pages)
3805       break;
3806
3807     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3808
3809     xx_change = *change;        // copy change data into temporary buffer
3810
3811     resetEventBits();           // reset bits; change page might have changed
3812
3813     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3814                                             -1, element);
3815
3816     *change = xx_change;
3817
3818     setEventFlagsFromEventBits(change);
3819
3820     if (real_chunk_size >= chunk_size)
3821       break;
3822   }
3823
3824   level->file_has_custom_elements = TRUE;
3825
3826   return real_chunk_size;
3827 }
3828
3829 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3830 {
3831   int element = getMappedElement(getFile16BitBE(file));
3832   int real_chunk_size = 2;
3833   struct ElementInfo *ei = &element_info[element];
3834   struct ElementGroupInfo *group = ei->group;
3835
3836   if (group == NULL)
3837     return -1;
3838
3839   xx_ei = *ei;          // copy element data into temporary buffer
3840   xx_group = *group;    // copy group data into temporary buffer
3841
3842   while (!checkEndOfFile(file))
3843   {
3844     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3845                                             -1, element);
3846
3847     if (real_chunk_size >= chunk_size)
3848       break;
3849   }
3850
3851   *ei = xx_ei;
3852   *group = xx_group;
3853
3854   level->file_has_custom_elements = TRUE;
3855
3856   return real_chunk_size;
3857 }
3858
3859 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3860 {
3861   int element = getMappedElement(getFile16BitBE(file));
3862   int real_chunk_size = 2;
3863   struct ElementInfo *ei = &element_info[element];
3864
3865   xx_ei = *ei;          // copy element data into temporary buffer
3866
3867   while (!checkEndOfFile(file))
3868   {
3869     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3870                                             -1, element);
3871
3872     if (real_chunk_size >= chunk_size)
3873       break;
3874   }
3875
3876   *ei = xx_ei;
3877
3878   level->file_has_custom_elements = TRUE;
3879
3880   return real_chunk_size;
3881 }
3882
3883 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3884                                       struct LevelFileInfo *level_file_info,
3885                                       boolean level_info_only)
3886 {
3887   char *filename = level_file_info->filename;
3888   char cookie[MAX_LINE_LEN];
3889   char chunk_name[CHUNK_ID_LEN + 1];
3890   int chunk_size;
3891   File *file;
3892
3893   if (!(file = openFile(filename, MODE_READ)))
3894   {
3895     level->no_valid_file = TRUE;
3896     level->no_level_file = TRUE;
3897
3898     if (level_info_only)
3899       return;
3900
3901     Warn("cannot read level '%s' -- using empty level", filename);
3902
3903     if (!setup.editor.use_template_for_new_levels)
3904       return;
3905
3906     // if level file not found, try to initialize level data from template
3907     filename = getGlobalLevelTemplateFilename();
3908
3909     if (!(file = openFile(filename, MODE_READ)))
3910       return;
3911
3912     // default: for empty levels, use level template for custom elements
3913     level->use_custom_template = TRUE;
3914
3915     level->no_valid_file = FALSE;
3916   }
3917
3918   getFileChunkBE(file, chunk_name, NULL);
3919   if (strEqual(chunk_name, "RND1"))
3920   {
3921     getFile32BitBE(file);               // not used
3922
3923     getFileChunkBE(file, chunk_name, NULL);
3924     if (!strEqual(chunk_name, "CAVE"))
3925     {
3926       level->no_valid_file = TRUE;
3927
3928       Warn("unknown format of level file '%s'", filename);
3929
3930       closeFile(file);
3931
3932       return;
3933     }
3934   }
3935   else  // check for pre-2.0 file format with cookie string
3936   {
3937     strcpy(cookie, chunk_name);
3938     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3939       cookie[4] = '\0';
3940     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3941       cookie[strlen(cookie) - 1] = '\0';
3942
3943     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3944     {
3945       level->no_valid_file = TRUE;
3946
3947       Warn("unknown format of level file '%s'", filename);
3948
3949       closeFile(file);
3950
3951       return;
3952     }
3953
3954     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3955     {
3956       level->no_valid_file = TRUE;
3957
3958       Warn("unsupported version of level file '%s'", filename);
3959
3960       closeFile(file);
3961
3962       return;
3963     }
3964
3965     // pre-2.0 level files have no game version, so use file version here
3966     level->game_version = level->file_version;
3967   }
3968
3969   if (level->file_version < FILE_VERSION_1_2)
3970   {
3971     // level files from versions before 1.2.0 without chunk structure
3972     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3973     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3974   }
3975   else
3976   {
3977     static struct
3978     {
3979       char *name;
3980       int size;
3981       int (*loader)(File *, int, struct LevelInfo *);
3982     }
3983     chunk_info[] =
3984     {
3985       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
3986       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
3987       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
3988       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
3989       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
3990       { "INFO", -1,                     LoadLevel_INFO },
3991       { "BODY", -1,                     LoadLevel_BODY },
3992       { "CONT", -1,                     LoadLevel_CONT },
3993       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
3994       { "CNT3", -1,                     LoadLevel_CNT3 },
3995       { "CUS1", -1,                     LoadLevel_CUS1 },
3996       { "CUS2", -1,                     LoadLevel_CUS2 },
3997       { "CUS3", -1,                     LoadLevel_CUS3 },
3998       { "CUS4", -1,                     LoadLevel_CUS4 },
3999       { "GRP1", -1,                     LoadLevel_GRP1 },
4000       { "CONF", -1,                     LoadLevel_CONF },
4001       { "ELEM", -1,                     LoadLevel_ELEM },
4002       { "NOTE", -1,                     LoadLevel_NOTE },
4003       { "CUSX", -1,                     LoadLevel_CUSX },
4004       { "GRPX", -1,                     LoadLevel_GRPX },
4005       { "EMPX", -1,                     LoadLevel_EMPX },
4006
4007       {  NULL,  0,                      NULL }
4008     };
4009
4010     while (getFileChunkBE(file, chunk_name, &chunk_size))
4011     {
4012       int i = 0;
4013
4014       while (chunk_info[i].name != NULL &&
4015              !strEqual(chunk_name, chunk_info[i].name))
4016         i++;
4017
4018       if (chunk_info[i].name == NULL)
4019       {
4020         Warn("unknown chunk '%s' in level file '%s'",
4021              chunk_name, filename);
4022
4023         ReadUnusedBytesFromFile(file, chunk_size);
4024       }
4025       else if (chunk_info[i].size != -1 &&
4026                chunk_info[i].size != chunk_size)
4027       {
4028         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4029              chunk_size, chunk_name, filename);
4030
4031         ReadUnusedBytesFromFile(file, chunk_size);
4032       }
4033       else
4034       {
4035         // call function to load this level chunk
4036         int chunk_size_expected =
4037           (chunk_info[i].loader)(file, chunk_size, level);
4038
4039         if (chunk_size_expected < 0)
4040         {
4041           Warn("error reading chunk '%s' in level file '%s'",
4042                chunk_name, filename);
4043
4044           break;
4045         }
4046
4047         // the size of some chunks cannot be checked before reading other
4048         // chunks first (like "HEAD" and "BODY") that contain some header
4049         // information, so check them here
4050         if (chunk_size_expected != chunk_size)
4051         {
4052           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4053                chunk_size, chunk_name, filename);
4054
4055           break;
4056         }
4057       }
4058     }
4059   }
4060
4061   closeFile(file);
4062 }
4063
4064
4065 // ----------------------------------------------------------------------------
4066 // functions for loading BD level
4067 // ----------------------------------------------------------------------------
4068
4069 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4070 {
4071   struct LevelInfo_BD *level_bd = level->native_bd_level;
4072   GdCave *cave = NULL;  // will be changed below
4073   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4074   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4075   int x, y;
4076
4077   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4078
4079   // cave and map newly allocated when set to defaults above
4080   cave = level_bd->cave;
4081
4082   // level type
4083   cave->intermission                    = level->bd_intermission;
4084
4085   // level settings
4086   cave->level_time[0]                   = level->time;
4087   cave->level_diamonds[0]               = level->gems_needed;
4088
4089   // game timing
4090   cave->scheduling                      = level->bd_scheduling_type;
4091   cave->pal_timing                      = level->bd_pal_timing;
4092   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4093   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4094   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4095   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4096
4097   // scores
4098   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4099   cave->diamond_value                   = level->score[SC_EMERALD];
4100   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4101
4102   // compatibility settings
4103   cave->lineshift                       = level->bd_line_shifting_borders;
4104   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4105   cave->short_explosions                = level->bd_short_explosions;
4106   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4107
4108   // player properties
4109   cave->diagonal_movements              = level->bd_diagonal_movements;
4110   cave->active_is_first_found           = level->bd_topmost_player_active;
4111   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4112   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4113   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4114   cave->snap_element                    = map_element_RND_to_BD_cave(level->bd_snap_element);
4115
4116   // element properties
4117   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4118   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4119   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4120   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4121   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4122   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4123   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4124   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4125   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4126
4127   cave->magic_diamond_to                = map_element_RND_to_BD_cave(level->bd_magic_wall_diamond_to);
4128   cave->magic_stone_to                  = map_element_RND_to_BD_cave(level->bd_magic_wall_rock_to);
4129   cave->magic_mega_stone_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_mega_rock_to);
4130   cave->magic_nut_to                    = map_element_RND_to_BD_cave(level->bd_magic_wall_nut_to);
4131   cave->magic_nitro_pack_to             = map_element_RND_to_BD_cave(level->bd_magic_wall_nitro_pack_to);
4132   cave->magic_flying_diamond_to         = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_diamond_to);
4133   cave->magic_flying_stone_to           = map_element_RND_to_BD_cave(level->bd_magic_wall_flying_rock_to);
4134
4135   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4136   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4137   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4138   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4139   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4140   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4141   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4142   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4143   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4144   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4145   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4146
4147   cave->amoeba_too_big_effect           = map_element_RND_to_BD_cave(level->bd_amoeba_content_too_big);
4148   cave->amoeba_enclosed_effect          = map_element_RND_to_BD_cave(level->bd_amoeba_content_enclosed);
4149   cave->amoeba_2_too_big_effect         = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_too_big);
4150   cave->amoeba_2_enclosed_effect        = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_enclosed);
4151   cave->amoeba_2_explosion_effect       = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_exploding);
4152   cave->amoeba_2_looks_like             = map_element_RND_to_BD_cave(level->bd_amoeba_2_content_looks_like);
4153
4154   cave->slime_predictable               = level->bd_slime_is_predictable;
4155   cave->slime_correct_random            = level->bd_slime_correct_random;
4156   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4157   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4158   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4159   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4160   cave->slime_eats_1                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_1);
4161   cave->slime_converts_1                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_1);
4162   cave->slime_eats_2                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_2);
4163   cave->slime_converts_2                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_2);
4164   cave->slime_eats_3                    = map_element_RND_to_BD_cave(level->bd_slime_eats_element_3);
4165   cave->slime_converts_3                = map_element_RND_to_BD_cave(level->bd_slime_converts_to_element_3);
4166
4167   cave->acid_eats_this                  = map_element_RND_to_BD_cave(level->bd_acid_eats_element);
4168   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4169   cave->acid_turns_to                   = map_element_RND_to_BD_cave(level->bd_acid_turns_to_element);
4170
4171   cave->biter_delay_frame               = level->bd_biter_move_delay;
4172   cave->biter_eat                       = map_element_RND_to_BD_cave(level->bd_biter_eats_element);
4173
4174   cave->bladder_converts_by             = map_element_RND_to_BD_cave(level->bd_bladder_converts_by_element);
4175
4176   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4177
4178   cave->replicators_active              = level->bd_replicators_active;
4179   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4180
4181   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4182   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4183
4184   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4185
4186   cave->nut_turns_to_when_crushed       = map_element_RND_to_BD_cave(level->bd_nut_content);
4187
4188   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4189   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4190   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4191
4192   // level name
4193   strncpy(cave->name, level->name, sizeof(GdString));
4194   cave->name[sizeof(GdString) - 1] = '\0';
4195
4196   // playfield elements
4197   for (x = 0; x < cave->w; x++)
4198     for (y = 0; y < cave->h; y++)
4199       cave->map[y][x] = map_element_RND_to_BD_cave(level->field[x][y]);
4200 }
4201
4202 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4203 {
4204   struct LevelInfo_BD *level_bd = level->native_bd_level;
4205   GdCave *cave = level_bd->cave;
4206   int bd_level_nr = level_bd->level_nr;
4207   int x, y;
4208
4209   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4210   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4211
4212   // level type
4213   level->bd_intermission                = cave->intermission;
4214
4215   // level settings
4216   level->time                           = cave->level_time[bd_level_nr];
4217   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4218
4219   // game timing
4220   level->bd_scheduling_type             = cave->scheduling;
4221   level->bd_pal_timing                  = cave->pal_timing;
4222   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4223   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4224   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4225   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4226
4227   // scores
4228   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4229   level->score[SC_EMERALD]              = cave->diamond_value;
4230   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4231
4232   // compatibility settings
4233   level->bd_line_shifting_borders       = cave->lineshift;
4234   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4235   level->bd_short_explosions            = cave->short_explosions;
4236   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4237
4238   // player properties
4239   level->bd_diagonal_movements          = cave->diagonal_movements;
4240   level->bd_topmost_player_active       = cave->active_is_first_found;
4241   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4242   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4243   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4244   level->bd_snap_element                = map_element_BD_to_RND_cave(cave->snap_element);
4245
4246   // element properties
4247   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4248   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4249   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4250   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4251   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4252   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4253   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4254   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4255   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4256
4257   level->bd_magic_wall_diamond_to       = map_element_BD_to_RND_cave(cave->magic_diamond_to);
4258   level->bd_magic_wall_rock_to          = map_element_BD_to_RND_cave(cave->magic_stone_to);
4259   level->bd_magic_wall_mega_rock_to     = map_element_BD_to_RND_cave(cave->magic_mega_stone_to);
4260   level->bd_magic_wall_nut_to           = map_element_BD_to_RND_cave(cave->magic_nut_to);
4261   level->bd_magic_wall_nitro_pack_to    = map_element_BD_to_RND_cave(cave->magic_nitro_pack_to);
4262   level->bd_magic_wall_flying_diamond_to= map_element_BD_to_RND_cave(cave->magic_flying_diamond_to);
4263   level->bd_magic_wall_flying_rock_to   = map_element_BD_to_RND_cave(cave->magic_flying_stone_to);
4264
4265   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4266   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4267   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4268   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4269   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4270   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4271   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4272   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4273   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4274   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4275   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4276
4277   level->bd_amoeba_content_too_big      = map_element_BD_to_RND_cave(cave->amoeba_too_big_effect);
4278   level->bd_amoeba_content_enclosed     = map_element_BD_to_RND_cave(cave->amoeba_enclosed_effect);
4279   level->bd_amoeba_2_content_too_big    = map_element_BD_to_RND_cave(cave->amoeba_2_too_big_effect);
4280   level->bd_amoeba_2_content_enclosed   = map_element_BD_to_RND_cave(cave->amoeba_2_enclosed_effect);
4281   level->bd_amoeba_2_content_exploding  = map_element_BD_to_RND_cave(cave->amoeba_2_explosion_effect);
4282   level->bd_amoeba_2_content_looks_like = map_element_BD_to_RND_cave(cave->amoeba_2_looks_like);
4283
4284   level->bd_slime_is_predictable        = cave->slime_predictable;
4285   level->bd_slime_correct_random        = cave->slime_correct_random;
4286   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4287   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4288   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4289   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4290   level->bd_slime_eats_element_1        = map_element_BD_to_RND_cave(cave->slime_eats_1);
4291   level->bd_slime_converts_to_element_1 = map_element_BD_to_RND_cave(cave->slime_converts_1);
4292   level->bd_slime_eats_element_2        = map_element_BD_to_RND_cave(cave->slime_eats_2);
4293   level->bd_slime_converts_to_element_2 = map_element_BD_to_RND_cave(cave->slime_converts_2);
4294   level->bd_slime_eats_element_3        = map_element_BD_to_RND_cave(cave->slime_eats_3);
4295   level->bd_slime_converts_to_element_3 = map_element_BD_to_RND_cave(cave->slime_converts_3);
4296
4297   level->bd_acid_eats_element           = map_element_BD_to_RND_cave(cave->acid_eats_this);
4298   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4299   level->bd_acid_turns_to_element       = map_element_BD_to_RND_cave(cave->acid_turns_to);
4300
4301   level->bd_biter_move_delay            = cave->biter_delay_frame;
4302   level->bd_biter_eats_element          = map_element_BD_to_RND_cave(cave->biter_eat);
4303
4304   level->bd_bladder_converts_by_element = map_element_BD_to_RND_cave(cave->bladder_converts_by);
4305
4306   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4307
4308   level->bd_replicators_active          = cave->replicators_active;
4309   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4310
4311   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4312   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4313
4314   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4315
4316   level->bd_nut_content                 = map_element_BD_to_RND_cave(cave->nut_turns_to_when_crushed);
4317
4318   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4319   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4320   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4321
4322   // level name
4323   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4324
4325   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4326   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4327
4328   // playfield elements
4329   for (x = 0; x < level->fieldx; x++)
4330     for (y = 0; y < level->fieldy; y++)
4331       level->field[x][y] = map_element_BD_to_RND_cave(cave->map[y][x]);
4332
4333   checked_free(cave_name);
4334 }
4335
4336 static void setTapeInfoToDefaults(void);
4337
4338 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4339 {
4340   struct LevelInfo_BD *level_bd = level->native_bd_level;
4341   GdCave *cave = level_bd->cave;
4342   GdReplay *replay = level_bd->replay;
4343   int i;
4344
4345   if (replay == NULL)
4346     return;
4347
4348   // always start with reliable default values
4349   setTapeInfoToDefaults();
4350
4351   tape.level_nr = level_nr;             // (currently not used)
4352   tape.random_seed = replay->seed;
4353
4354   TapeSetDateFromIsoDateString(replay->date);
4355
4356   tape.counter = 0;
4357   tape.pos[tape.counter].delay = 0;
4358
4359   tape.bd_replay = TRUE;
4360
4361   // all time calculations only used to display approximate tape time
4362   int cave_speed = cave->speed;
4363   int milliseconds_game = 0;
4364   int milliseconds_elapsed = 20;
4365
4366   for (i = 0; i < replay->movements->len; i++)
4367   {
4368     int replay_action = replay->movements->data[i];
4369     int tape_action = map_action_BD_to_RND(replay_action);
4370     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4371     boolean success = 0;
4372
4373     while (1)
4374     {
4375       success = TapeAddAction(action);
4376
4377       milliseconds_game += milliseconds_elapsed;
4378
4379       if (milliseconds_game >= cave_speed)
4380       {
4381         milliseconds_game -= cave_speed;
4382
4383         break;
4384       }
4385     }
4386
4387     tape.counter++;
4388     tape.pos[tape.counter].delay = 0;
4389     tape.pos[tape.counter].action[0] = 0;
4390
4391     if (!success)
4392     {
4393       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4394
4395       break;
4396     }
4397   }
4398
4399   TapeHaltRecording();
4400 }
4401
4402
4403 // ----------------------------------------------------------------------------
4404 // functions for loading EM level
4405 // ----------------------------------------------------------------------------
4406
4407 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4408 {
4409   static int ball_xy[8][2] =
4410   {
4411     { 0, 0 },
4412     { 1, 0 },
4413     { 2, 0 },
4414     { 0, 1 },
4415     { 2, 1 },
4416     { 0, 2 },
4417     { 1, 2 },
4418     { 2, 2 },
4419   };
4420   struct LevelInfo_EM *level_em = level->native_em_level;
4421   struct CAVE *cav = level_em->cav;
4422   int i, j, x, y;
4423
4424   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4425   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4426
4427   cav->time_seconds     = level->time;
4428   cav->gems_needed      = level->gems_needed;
4429
4430   cav->emerald_score    = level->score[SC_EMERALD];
4431   cav->diamond_score    = level->score[SC_DIAMOND];
4432   cav->alien_score      = level->score[SC_ROBOT];
4433   cav->tank_score       = level->score[SC_SPACESHIP];
4434   cav->bug_score        = level->score[SC_BUG];
4435   cav->eater_score      = level->score[SC_YAMYAM];
4436   cav->nut_score        = level->score[SC_NUT];
4437   cav->dynamite_score   = level->score[SC_DYNAMITE];
4438   cav->key_score        = level->score[SC_KEY];
4439   cav->exit_score       = level->score[SC_TIME_BONUS];
4440
4441   cav->num_eater_arrays = level->num_yamyam_contents;
4442
4443   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4444     for (y = 0; y < 3; y++)
4445       for (x = 0; x < 3; x++)
4446         cav->eater_array[i][y * 3 + x] =
4447           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4448
4449   cav->amoeba_time              = level->amoeba_speed;
4450   cav->wonderwall_time          = level->time_magic_wall;
4451   cav->wheel_time               = level->time_wheel;
4452
4453   cav->android_move_time        = level->android_move_time;
4454   cav->android_clone_time       = level->android_clone_time;
4455   cav->ball_random              = level->ball_random;
4456   cav->ball_active              = level->ball_active_initial;
4457   cav->ball_time                = level->ball_time;
4458   cav->num_ball_arrays          = level->num_ball_contents;
4459
4460   cav->lenses_score             = level->lenses_score;
4461   cav->magnify_score            = level->magnify_score;
4462   cav->slurp_score              = level->slurp_score;
4463
4464   cav->lenses_time              = level->lenses_time;
4465   cav->magnify_time             = level->magnify_time;
4466
4467   cav->wind_time = 9999;
4468   cav->wind_direction =
4469     map_direction_RND_to_EM(level->wind_direction_initial);
4470
4471   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4472     for (j = 0; j < 8; j++)
4473       cav->ball_array[i][j] =
4474         map_element_RND_to_EM_cave(level->ball_content[i].
4475                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4476
4477   map_android_clone_elements_RND_to_EM(level);
4478
4479   // first fill the complete playfield with the empty space element
4480   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4481     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4482       cav->cave[x][y] = Cblank;
4483
4484   // then copy the real level contents from level file into the playfield
4485   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4486   {
4487     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4488
4489     if (level->field[x][y] == EL_AMOEBA_DEAD)
4490       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4491
4492     cav->cave[x][y] = new_element;
4493   }
4494
4495   for (i = 0; i < MAX_PLAYERS; i++)
4496   {
4497     cav->player_x[i] = -1;
4498     cav->player_y[i] = -1;
4499   }
4500
4501   // initialize player positions and delete players from the playfield
4502   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4503   {
4504     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4505     {
4506       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4507
4508       cav->player_x[player_nr] = x;
4509       cav->player_y[player_nr] = y;
4510
4511       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4512     }
4513   }
4514 }
4515
4516 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4517 {
4518   static int ball_xy[8][2] =
4519   {
4520     { 0, 0 },
4521     { 1, 0 },
4522     { 2, 0 },
4523     { 0, 1 },
4524     { 2, 1 },
4525     { 0, 2 },
4526     { 1, 2 },
4527     { 2, 2 },
4528   };
4529   struct LevelInfo_EM *level_em = level->native_em_level;
4530   struct CAVE *cav = level_em->cav;
4531   int i, j, x, y;
4532
4533   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4534   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4535
4536   level->time        = cav->time_seconds;
4537   level->gems_needed = cav->gems_needed;
4538
4539   sprintf(level->name, "Level %d", level->file_info.nr);
4540
4541   level->score[SC_EMERALD]      = cav->emerald_score;
4542   level->score[SC_DIAMOND]      = cav->diamond_score;
4543   level->score[SC_ROBOT]        = cav->alien_score;
4544   level->score[SC_SPACESHIP]    = cav->tank_score;
4545   level->score[SC_BUG]          = cav->bug_score;
4546   level->score[SC_YAMYAM]       = cav->eater_score;
4547   level->score[SC_NUT]          = cav->nut_score;
4548   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4549   level->score[SC_KEY]          = cav->key_score;
4550   level->score[SC_TIME_BONUS]   = cav->exit_score;
4551
4552   level->num_yamyam_contents    = cav->num_eater_arrays;
4553
4554   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4555     for (y = 0; y < 3; y++)
4556       for (x = 0; x < 3; x++)
4557         level->yamyam_content[i].e[x][y] =
4558           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4559
4560   level->amoeba_speed           = cav->amoeba_time;
4561   level->time_magic_wall        = cav->wonderwall_time;
4562   level->time_wheel             = cav->wheel_time;
4563
4564   level->android_move_time      = cav->android_move_time;
4565   level->android_clone_time     = cav->android_clone_time;
4566   level->ball_random            = cav->ball_random;
4567   level->ball_active_initial    = cav->ball_active;
4568   level->ball_time              = cav->ball_time;
4569   level->num_ball_contents      = cav->num_ball_arrays;
4570
4571   level->lenses_score           = cav->lenses_score;
4572   level->magnify_score          = cav->magnify_score;
4573   level->slurp_score            = cav->slurp_score;
4574
4575   level->lenses_time            = cav->lenses_time;
4576   level->magnify_time           = cav->magnify_time;
4577
4578   level->wind_direction_initial =
4579     map_direction_EM_to_RND(cav->wind_direction);
4580
4581   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4582     for (j = 0; j < 8; j++)
4583       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4584         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4585
4586   map_android_clone_elements_EM_to_RND(level);
4587
4588   // convert the playfield (some elements need special treatment)
4589   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4590   {
4591     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4592
4593     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4594       new_element = EL_AMOEBA_DEAD;
4595
4596     level->field[x][y] = new_element;
4597   }
4598
4599   for (i = 0; i < MAX_PLAYERS; i++)
4600   {
4601     // in case of all players set to the same field, use the first player
4602     int nr = MAX_PLAYERS - i - 1;
4603     int jx = cav->player_x[nr];
4604     int jy = cav->player_y[nr];
4605
4606     if (jx != -1 && jy != -1)
4607       level->field[jx][jy] = EL_PLAYER_1 + nr;
4608   }
4609
4610   // time score is counted for each 10 seconds left in Emerald Mine levels
4611   level->time_score_base = 10;
4612 }
4613
4614
4615 // ----------------------------------------------------------------------------
4616 // functions for loading SP level
4617 // ----------------------------------------------------------------------------
4618
4619 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4620 {
4621   struct LevelInfo_SP *level_sp = level->native_sp_level;
4622   LevelInfoType *header = &level_sp->header;
4623   int i, x, y;
4624
4625   level_sp->width  = level->fieldx;
4626   level_sp->height = level->fieldy;
4627
4628   for (x = 0; x < level->fieldx; x++)
4629     for (y = 0; y < level->fieldy; y++)
4630       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4631
4632   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4633
4634   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4635     header->LevelTitle[i] = level->name[i];
4636   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4637
4638   header->InfotronsNeeded = level->gems_needed;
4639
4640   header->SpecialPortCount = 0;
4641
4642   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4643   {
4644     boolean gravity_port_found = FALSE;
4645     boolean gravity_port_valid = FALSE;
4646     int gravity_port_flag;
4647     int gravity_port_base_element;
4648     int element = level->field[x][y];
4649
4650     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4651         element <= EL_SP_GRAVITY_ON_PORT_UP)
4652     {
4653       gravity_port_found = TRUE;
4654       gravity_port_valid = TRUE;
4655       gravity_port_flag = 1;
4656       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4657     }
4658     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4659              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4660     {
4661       gravity_port_found = TRUE;
4662       gravity_port_valid = TRUE;
4663       gravity_port_flag = 0;
4664       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4665     }
4666     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4667              element <= EL_SP_GRAVITY_PORT_UP)
4668     {
4669       // change R'n'D style gravity inverting special port to normal port
4670       // (there are no gravity inverting ports in native Supaplex engine)
4671
4672       gravity_port_found = TRUE;
4673       gravity_port_valid = FALSE;
4674       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4675     }
4676
4677     if (gravity_port_found)
4678     {
4679       if (gravity_port_valid &&
4680           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4681       {
4682         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4683
4684         port->PortLocation = (y * level->fieldx + x) * 2;
4685         port->Gravity = gravity_port_flag;
4686
4687         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4688
4689         header->SpecialPortCount++;
4690       }
4691       else
4692       {
4693         // change special gravity port to normal port
4694
4695         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4696       }
4697
4698       level_sp->playfield[x][y] = element - EL_SP_START;
4699     }
4700   }
4701 }
4702
4703 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4704 {
4705   struct LevelInfo_SP *level_sp = level->native_sp_level;
4706   LevelInfoType *header = &level_sp->header;
4707   boolean num_invalid_elements = 0;
4708   int i, j, x, y;
4709
4710   level->fieldx = level_sp->width;
4711   level->fieldy = level_sp->height;
4712
4713   for (x = 0; x < level->fieldx; x++)
4714   {
4715     for (y = 0; y < level->fieldy; y++)
4716     {
4717       int element_old = level_sp->playfield[x][y];
4718       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4719
4720       if (element_new == EL_UNKNOWN)
4721       {
4722         num_invalid_elements++;
4723
4724         Debug("level:native:SP", "invalid element %d at position %d, %d",
4725               element_old, x, y);
4726       }
4727
4728       level->field[x][y] = element_new;
4729     }
4730   }
4731
4732   if (num_invalid_elements > 0)
4733     Warn("found %d invalid elements%s", num_invalid_elements,
4734          (!options.debug ? " (use '--debug' for more details)" : ""));
4735
4736   for (i = 0; i < MAX_PLAYERS; i++)
4737     level->initial_player_gravity[i] =
4738       (header->InitialGravity == 1 ? TRUE : FALSE);
4739
4740   // skip leading spaces
4741   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4742     if (header->LevelTitle[i] != ' ')
4743       break;
4744
4745   // copy level title
4746   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4747     level->name[j] = header->LevelTitle[i];
4748   level->name[j] = '\0';
4749
4750   // cut trailing spaces
4751   for (; j > 0; j--)
4752     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4753       level->name[j - 1] = '\0';
4754
4755   level->gems_needed = header->InfotronsNeeded;
4756
4757   for (i = 0; i < header->SpecialPortCount; i++)
4758   {
4759     SpecialPortType *port = &header->SpecialPort[i];
4760     int port_location = port->PortLocation;
4761     int gravity = port->Gravity;
4762     int port_x, port_y, port_element;
4763
4764     port_x = (port_location / 2) % level->fieldx;
4765     port_y = (port_location / 2) / level->fieldx;
4766
4767     if (port_x < 0 || port_x >= level->fieldx ||
4768         port_y < 0 || port_y >= level->fieldy)
4769     {
4770       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4771
4772       continue;
4773     }
4774
4775     port_element = level->field[port_x][port_y];
4776
4777     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4778         port_element > EL_SP_GRAVITY_PORT_UP)
4779     {
4780       Warn("no special port at position (%d, %d)", port_x, port_y);
4781
4782       continue;
4783     }
4784
4785     // change previous (wrong) gravity inverting special port to either
4786     // gravity enabling special port or gravity disabling special port
4787     level->field[port_x][port_y] +=
4788       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4789        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4790   }
4791
4792   // change special gravity ports without database entries to normal ports
4793   for (x = 0; x < level->fieldx; x++)
4794     for (y = 0; y < level->fieldy; y++)
4795       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4796           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4797         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4798
4799   level->time = 0;                      // no time limit
4800   level->amoeba_speed = 0;
4801   level->time_magic_wall = 0;
4802   level->time_wheel = 0;
4803   level->amoeba_content = EL_EMPTY;
4804
4805   // original Supaplex does not use score values -- rate by playing time
4806   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4807     level->score[i] = 0;
4808
4809   level->rate_time_over_score = TRUE;
4810
4811   // there are no yamyams in supaplex levels
4812   for (i = 0; i < level->num_yamyam_contents; i++)
4813     for (x = 0; x < 3; x++)
4814       for (y = 0; y < 3; y++)
4815         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4816 }
4817
4818 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4819 {
4820   struct LevelInfo_SP *level_sp = level->native_sp_level;
4821   struct DemoInfo_SP *demo = &level_sp->demo;
4822   int i, j;
4823
4824   // always start with reliable default values
4825   demo->is_available = FALSE;
4826   demo->length = 0;
4827
4828   if (TAPE_IS_EMPTY(tape))
4829     return;
4830
4831   demo->level_nr = tape.level_nr;       // (currently not used)
4832
4833   level_sp->header.DemoRandomSeed = tape.random_seed;
4834
4835   demo->length = 0;
4836
4837   for (i = 0; i < tape.length; i++)
4838   {
4839     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4840     int demo_repeat = tape.pos[i].delay;
4841     int demo_entries = (demo_repeat + 15) / 16;
4842
4843     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4844     {
4845       Warn("tape truncated: size exceeds maximum SP demo size %d",
4846            SP_MAX_TAPE_LEN);
4847
4848       break;
4849     }
4850
4851     for (j = 0; j < demo_repeat / 16; j++)
4852       demo->data[demo->length++] = 0xf0 | demo_action;
4853
4854     if (demo_repeat % 16)
4855       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4856   }
4857
4858   demo->is_available = TRUE;
4859 }
4860
4861 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4862 {
4863   struct LevelInfo_SP *level_sp = level->native_sp_level;
4864   struct DemoInfo_SP *demo = &level_sp->demo;
4865   char *filename = level->file_info.filename;
4866   int i;
4867
4868   // always start with reliable default values
4869   setTapeInfoToDefaults();
4870
4871   if (!demo->is_available)
4872     return;
4873
4874   tape.level_nr = demo->level_nr;       // (currently not used)
4875   tape.random_seed = level_sp->header.DemoRandomSeed;
4876
4877   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4878
4879   tape.counter = 0;
4880   tape.pos[tape.counter].delay = 0;
4881
4882   for (i = 0; i < demo->length; i++)
4883   {
4884     int demo_action = demo->data[i] & 0x0f;
4885     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4886     int tape_action = map_key_SP_to_RND(demo_action);
4887     int tape_repeat = demo_repeat + 1;
4888     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4889     boolean success = 0;
4890     int j;
4891
4892     for (j = 0; j < tape_repeat; j++)
4893       success = TapeAddAction(action);
4894
4895     if (!success)
4896     {
4897       Warn("SP demo truncated: size exceeds maximum tape size %d",
4898            MAX_TAPE_LEN);
4899
4900       break;
4901     }
4902   }
4903
4904   TapeHaltRecording();
4905 }
4906
4907
4908 // ----------------------------------------------------------------------------
4909 // functions for loading MM level
4910 // ----------------------------------------------------------------------------
4911
4912 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4913 {
4914   struct LevelInfo_MM *level_mm = level->native_mm_level;
4915   int i, x, y;
4916
4917   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4918   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4919
4920   level_mm->time = level->time;
4921   level_mm->kettles_needed = level->gems_needed;
4922   level_mm->auto_count_kettles = level->auto_count_gems;
4923
4924   level_mm->mm_laser_red   = level->mm_laser_red;
4925   level_mm->mm_laser_green = level->mm_laser_green;
4926   level_mm->mm_laser_blue  = level->mm_laser_blue;
4927
4928   level_mm->df_laser_red   = level->df_laser_red;
4929   level_mm->df_laser_green = level->df_laser_green;
4930   level_mm->df_laser_blue  = level->df_laser_blue;
4931
4932   strcpy(level_mm->name, level->name);
4933   strcpy(level_mm->author, level->author);
4934
4935   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4936   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4937   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4938   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4939   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4940
4941   level_mm->amoeba_speed = level->amoeba_speed;
4942   level_mm->time_fuse    = level->mm_time_fuse;
4943   level_mm->time_bomb    = level->mm_time_bomb;
4944   level_mm->time_ball    = level->mm_time_ball;
4945   level_mm->time_block   = level->mm_time_block;
4946
4947   level_mm->num_ball_contents = level->num_mm_ball_contents;
4948   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4949   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4950   level_mm->explode_ball = level->explode_mm_ball;
4951
4952   for (i = 0; i < level->num_mm_ball_contents; i++)
4953     level_mm->ball_content[i] =
4954       map_element_RND_to_MM(level->mm_ball_content[i]);
4955
4956   for (x = 0; x < level->fieldx; x++)
4957     for (y = 0; y < level->fieldy; y++)
4958       Ur[x][y] =
4959         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4960 }
4961
4962 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
4963 {
4964   struct LevelInfo_MM *level_mm = level->native_mm_level;
4965   int i, x, y;
4966
4967   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
4968   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
4969
4970   level->time = level_mm->time;
4971   level->gems_needed = level_mm->kettles_needed;
4972   level->auto_count_gems = level_mm->auto_count_kettles;
4973
4974   level->mm_laser_red   = level_mm->mm_laser_red;
4975   level->mm_laser_green = level_mm->mm_laser_green;
4976   level->mm_laser_blue  = level_mm->mm_laser_blue;
4977
4978   level->df_laser_red   = level_mm->df_laser_red;
4979   level->df_laser_green = level_mm->df_laser_green;
4980   level->df_laser_blue  = level_mm->df_laser_blue;
4981
4982   strcpy(level->name, level_mm->name);
4983
4984   // only overwrite author from 'levelinfo.conf' if author defined in level
4985   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
4986     strcpy(level->author, level_mm->author);
4987
4988   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
4989   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
4990   level->score[SC_KEY]        = level_mm->score[SC_KEY];
4991   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
4992   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
4993
4994   level->amoeba_speed  = level_mm->amoeba_speed;
4995   level->mm_time_fuse  = level_mm->time_fuse;
4996   level->mm_time_bomb  = level_mm->time_bomb;
4997   level->mm_time_ball  = level_mm->time_ball;
4998   level->mm_time_block = level_mm->time_block;
4999
5000   level->num_mm_ball_contents = level_mm->num_ball_contents;
5001   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5002   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5003   level->explode_mm_ball = level_mm->explode_ball;
5004
5005   for (i = 0; i < level->num_mm_ball_contents; i++)
5006     level->mm_ball_content[i] =
5007       map_element_MM_to_RND(level_mm->ball_content[i]);
5008
5009   for (x = 0; x < level->fieldx; x++)
5010     for (y = 0; y < level->fieldy; y++)
5011       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5012 }
5013
5014
5015 // ----------------------------------------------------------------------------
5016 // functions for loading DC level
5017 // ----------------------------------------------------------------------------
5018
5019 #define DC_LEVEL_HEADER_SIZE            344
5020
5021 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5022                                         boolean init)
5023 {
5024   static int last_data_encoded;
5025   static int offset1;
5026   static int offset2;
5027   int diff;
5028   int diff_hi, diff_lo;
5029   int data_hi, data_lo;
5030   unsigned short data_decoded;
5031
5032   if (init)
5033   {
5034     last_data_encoded = 0;
5035     offset1 = -1;
5036     offset2 = 0;
5037
5038     return 0;
5039   }
5040
5041   diff = data_encoded - last_data_encoded;
5042   diff_hi = diff & ~0xff;
5043   diff_lo = diff &  0xff;
5044
5045   offset2 += diff_lo;
5046
5047   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5048   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5049   data_hi = data_hi & 0xff00;
5050
5051   data_decoded = data_hi | data_lo;
5052
5053   last_data_encoded = data_encoded;
5054
5055   offset1 = (offset1 + 1) % 31;
5056   offset2 = offset2 & 0xff;
5057
5058   return data_decoded;
5059 }
5060
5061 static int getMappedElement_DC(int element)
5062 {
5063   switch (element)
5064   {
5065     case 0x0000:
5066       element = EL_ROCK;
5067       break;
5068
5069       // 0x0117 - 0x036e: (?)
5070       // EL_DIAMOND
5071
5072       // 0x042d - 0x0684: (?)
5073       // EL_EMERALD
5074
5075     case 0x06f1:
5076       element = EL_NUT;
5077       break;
5078
5079     case 0x074c:
5080       element = EL_BOMB;
5081       break;
5082
5083     case 0x07a4:
5084       element = EL_PEARL;
5085       break;
5086
5087     case 0x0823:
5088       element = EL_CRYSTAL;
5089       break;
5090
5091     case 0x0e77:        // quicksand (boulder)
5092       element = EL_QUICKSAND_FAST_FULL;
5093       break;
5094
5095     case 0x0e99:        // slow quicksand (boulder)
5096       element = EL_QUICKSAND_FULL;
5097       break;
5098
5099     case 0x0ed2:
5100       element = EL_EM_EXIT_OPEN;
5101       break;
5102
5103     case 0x0ee3:
5104       element = EL_EM_EXIT_CLOSED;
5105       break;
5106
5107     case 0x0eeb:
5108       element = EL_EM_STEEL_EXIT_OPEN;
5109       break;
5110
5111     case 0x0efc:
5112       element = EL_EM_STEEL_EXIT_CLOSED;
5113       break;
5114
5115     case 0x0f4f:        // dynamite (lit 1)
5116       element = EL_EM_DYNAMITE_ACTIVE;
5117       break;
5118
5119     case 0x0f57:        // dynamite (lit 2)
5120       element = EL_EM_DYNAMITE_ACTIVE;
5121       break;
5122
5123     case 0x0f5f:        // dynamite (lit 3)
5124       element = EL_EM_DYNAMITE_ACTIVE;
5125       break;
5126
5127     case 0x0f67:        // dynamite (lit 4)
5128       element = EL_EM_DYNAMITE_ACTIVE;
5129       break;
5130
5131     case 0x0f81:
5132     case 0x0f82:
5133     case 0x0f83:
5134     case 0x0f84:
5135       element = EL_AMOEBA_WET;
5136       break;
5137
5138     case 0x0f85:
5139       element = EL_AMOEBA_DROP;
5140       break;
5141
5142     case 0x0fb9:
5143       element = EL_DC_MAGIC_WALL;
5144       break;
5145
5146     case 0x0fd0:
5147       element = EL_SPACESHIP_UP;
5148       break;
5149
5150     case 0x0fd9:
5151       element = EL_SPACESHIP_DOWN;
5152       break;
5153
5154     case 0x0ff1:
5155       element = EL_SPACESHIP_LEFT;
5156       break;
5157
5158     case 0x0ff9:
5159       element = EL_SPACESHIP_RIGHT;
5160       break;
5161
5162     case 0x1057:
5163       element = EL_BUG_UP;
5164       break;
5165
5166     case 0x1060:
5167       element = EL_BUG_DOWN;
5168       break;
5169
5170     case 0x1078:
5171       element = EL_BUG_LEFT;
5172       break;
5173
5174     case 0x1080:
5175       element = EL_BUG_RIGHT;
5176       break;
5177
5178     case 0x10de:
5179       element = EL_MOLE_UP;
5180       break;
5181
5182     case 0x10e7:
5183       element = EL_MOLE_DOWN;
5184       break;
5185
5186     case 0x10ff:
5187       element = EL_MOLE_LEFT;
5188       break;
5189
5190     case 0x1107:
5191       element = EL_MOLE_RIGHT;
5192       break;
5193
5194     case 0x11c0:
5195       element = EL_ROBOT;
5196       break;
5197
5198     case 0x13f5:
5199       element = EL_YAMYAM_UP;
5200       break;
5201
5202     case 0x1425:
5203       element = EL_SWITCHGATE_OPEN;
5204       break;
5205
5206     case 0x1426:
5207       element = EL_SWITCHGATE_CLOSED;
5208       break;
5209
5210     case 0x1437:
5211       element = EL_DC_SWITCHGATE_SWITCH_UP;
5212       break;
5213
5214     case 0x143a:
5215       element = EL_TIMEGATE_CLOSED;
5216       break;
5217
5218     case 0x144c:        // conveyor belt switch (green)
5219       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5220       break;
5221
5222     case 0x144f:        // conveyor belt switch (red)
5223       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5224       break;
5225
5226     case 0x1452:        // conveyor belt switch (blue)
5227       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5228       break;
5229
5230     case 0x145b:
5231       element = EL_CONVEYOR_BELT_3_MIDDLE;
5232       break;
5233
5234     case 0x1463:
5235       element = EL_CONVEYOR_BELT_3_LEFT;
5236       break;
5237
5238     case 0x146b:
5239       element = EL_CONVEYOR_BELT_3_RIGHT;
5240       break;
5241
5242     case 0x1473:
5243       element = EL_CONVEYOR_BELT_1_MIDDLE;
5244       break;
5245
5246     case 0x147b:
5247       element = EL_CONVEYOR_BELT_1_LEFT;
5248       break;
5249
5250     case 0x1483:
5251       element = EL_CONVEYOR_BELT_1_RIGHT;
5252       break;
5253
5254     case 0x148b:
5255       element = EL_CONVEYOR_BELT_4_MIDDLE;
5256       break;
5257
5258     case 0x1493:
5259       element = EL_CONVEYOR_BELT_4_LEFT;
5260       break;
5261
5262     case 0x149b:
5263       element = EL_CONVEYOR_BELT_4_RIGHT;
5264       break;
5265
5266     case 0x14ac:
5267       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5268       break;
5269
5270     case 0x14bd:
5271       element = EL_EXPANDABLE_WALL_VERTICAL;
5272       break;
5273
5274     case 0x14c6:
5275       element = EL_EXPANDABLE_WALL_ANY;
5276       break;
5277
5278     case 0x14ce:        // growing steel wall (left/right)
5279       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5280       break;
5281
5282     case 0x14df:        // growing steel wall (up/down)
5283       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5284       break;
5285
5286     case 0x14e8:        // growing steel wall (up/down/left/right)
5287       element = EL_EXPANDABLE_STEELWALL_ANY;
5288       break;
5289
5290     case 0x14e9:
5291       element = EL_SHIELD_DEADLY;
5292       break;
5293
5294     case 0x1501:
5295       element = EL_EXTRA_TIME;
5296       break;
5297
5298     case 0x154f:
5299       element = EL_ACID;
5300       break;
5301
5302     case 0x1577:
5303       element = EL_EMPTY_SPACE;
5304       break;
5305
5306     case 0x1578:        // quicksand (empty)
5307       element = EL_QUICKSAND_FAST_EMPTY;
5308       break;
5309
5310     case 0x1579:        // slow quicksand (empty)
5311       element = EL_QUICKSAND_EMPTY;
5312       break;
5313
5314       // 0x157c - 0x158b:
5315       // EL_SAND
5316
5317       // 0x1590 - 0x159f:
5318       // EL_DC_LANDMINE
5319
5320     case 0x15a0:
5321       element = EL_EM_DYNAMITE;
5322       break;
5323
5324     case 0x15a1:        // key (red)
5325       element = EL_EM_KEY_1;
5326       break;
5327
5328     case 0x15a2:        // key (yellow)
5329       element = EL_EM_KEY_2;
5330       break;
5331
5332     case 0x15a3:        // key (blue)
5333       element = EL_EM_KEY_4;
5334       break;
5335
5336     case 0x15a4:        // key (green)
5337       element = EL_EM_KEY_3;
5338       break;
5339
5340     case 0x15a5:        // key (white)
5341       element = EL_DC_KEY_WHITE;
5342       break;
5343
5344     case 0x15a6:
5345       element = EL_WALL_SLIPPERY;
5346       break;
5347
5348     case 0x15a7:
5349       element = EL_WALL;
5350       break;
5351
5352     case 0x15a8:        // wall (not round)
5353       element = EL_WALL;
5354       break;
5355
5356     case 0x15a9:        // (blue)
5357       element = EL_CHAR_A;
5358       break;
5359
5360     case 0x15aa:        // (blue)
5361       element = EL_CHAR_B;
5362       break;
5363
5364     case 0x15ab:        // (blue)
5365       element = EL_CHAR_C;
5366       break;
5367
5368     case 0x15ac:        // (blue)
5369       element = EL_CHAR_D;
5370       break;
5371
5372     case 0x15ad:        // (blue)
5373       element = EL_CHAR_E;
5374       break;
5375
5376     case 0x15ae:        // (blue)
5377       element = EL_CHAR_F;
5378       break;
5379
5380     case 0x15af:        // (blue)
5381       element = EL_CHAR_G;
5382       break;
5383
5384     case 0x15b0:        // (blue)
5385       element = EL_CHAR_H;
5386       break;
5387
5388     case 0x15b1:        // (blue)
5389       element = EL_CHAR_I;
5390       break;
5391
5392     case 0x15b2:        // (blue)
5393       element = EL_CHAR_J;
5394       break;
5395
5396     case 0x15b3:        // (blue)
5397       element = EL_CHAR_K;
5398       break;
5399
5400     case 0x15b4:        // (blue)
5401       element = EL_CHAR_L;
5402       break;
5403
5404     case 0x15b5:        // (blue)
5405       element = EL_CHAR_M;
5406       break;
5407
5408     case 0x15b6:        // (blue)
5409       element = EL_CHAR_N;
5410       break;
5411
5412     case 0x15b7:        // (blue)
5413       element = EL_CHAR_O;
5414       break;
5415
5416     case 0x15b8:        // (blue)
5417       element = EL_CHAR_P;
5418       break;
5419
5420     case 0x15b9:        // (blue)
5421       element = EL_CHAR_Q;
5422       break;
5423
5424     case 0x15ba:        // (blue)
5425       element = EL_CHAR_R;
5426       break;
5427
5428     case 0x15bb:        // (blue)
5429       element = EL_CHAR_S;
5430       break;
5431
5432     case 0x15bc:        // (blue)
5433       element = EL_CHAR_T;
5434       break;
5435
5436     case 0x15bd:        // (blue)
5437       element = EL_CHAR_U;
5438       break;
5439
5440     case 0x15be:        // (blue)
5441       element = EL_CHAR_V;
5442       break;
5443
5444     case 0x15bf:        // (blue)
5445       element = EL_CHAR_W;
5446       break;
5447
5448     case 0x15c0:        // (blue)
5449       element = EL_CHAR_X;
5450       break;
5451
5452     case 0x15c1:        // (blue)
5453       element = EL_CHAR_Y;
5454       break;
5455
5456     case 0x15c2:        // (blue)
5457       element = EL_CHAR_Z;
5458       break;
5459
5460     case 0x15c3:        // (blue)
5461       element = EL_CHAR_AUMLAUT;
5462       break;
5463
5464     case 0x15c4:        // (blue)
5465       element = EL_CHAR_OUMLAUT;
5466       break;
5467
5468     case 0x15c5:        // (blue)
5469       element = EL_CHAR_UUMLAUT;
5470       break;
5471
5472     case 0x15c6:        // (blue)
5473       element = EL_CHAR_0;
5474       break;
5475
5476     case 0x15c7:        // (blue)
5477       element = EL_CHAR_1;
5478       break;
5479
5480     case 0x15c8:        // (blue)
5481       element = EL_CHAR_2;
5482       break;
5483
5484     case 0x15c9:        // (blue)
5485       element = EL_CHAR_3;
5486       break;
5487
5488     case 0x15ca:        // (blue)
5489       element = EL_CHAR_4;
5490       break;
5491
5492     case 0x15cb:        // (blue)
5493       element = EL_CHAR_5;
5494       break;
5495
5496     case 0x15cc:        // (blue)
5497       element = EL_CHAR_6;
5498       break;
5499
5500     case 0x15cd:        // (blue)
5501       element = EL_CHAR_7;
5502       break;
5503
5504     case 0x15ce:        // (blue)
5505       element = EL_CHAR_8;
5506       break;
5507
5508     case 0x15cf:        // (blue)
5509       element = EL_CHAR_9;
5510       break;
5511
5512     case 0x15d0:        // (blue)
5513       element = EL_CHAR_PERIOD;
5514       break;
5515
5516     case 0x15d1:        // (blue)
5517       element = EL_CHAR_EXCLAM;
5518       break;
5519
5520     case 0x15d2:        // (blue)
5521       element = EL_CHAR_COLON;
5522       break;
5523
5524     case 0x15d3:        // (blue)
5525       element = EL_CHAR_LESS;
5526       break;
5527
5528     case 0x15d4:        // (blue)
5529       element = EL_CHAR_GREATER;
5530       break;
5531
5532     case 0x15d5:        // (blue)
5533       element = EL_CHAR_QUESTION;
5534       break;
5535
5536     case 0x15d6:        // (blue)
5537       element = EL_CHAR_COPYRIGHT;
5538       break;
5539
5540     case 0x15d7:        // (blue)
5541       element = EL_CHAR_UP;
5542       break;
5543
5544     case 0x15d8:        // (blue)
5545       element = EL_CHAR_DOWN;
5546       break;
5547
5548     case 0x15d9:        // (blue)
5549       element = EL_CHAR_BUTTON;
5550       break;
5551
5552     case 0x15da:        // (blue)
5553       element = EL_CHAR_PLUS;
5554       break;
5555
5556     case 0x15db:        // (blue)
5557       element = EL_CHAR_MINUS;
5558       break;
5559
5560     case 0x15dc:        // (blue)
5561       element = EL_CHAR_APOSTROPHE;
5562       break;
5563
5564     case 0x15dd:        // (blue)
5565       element = EL_CHAR_PARENLEFT;
5566       break;
5567
5568     case 0x15de:        // (blue)
5569       element = EL_CHAR_PARENRIGHT;
5570       break;
5571
5572     case 0x15df:        // (green)
5573       element = EL_CHAR_A;
5574       break;
5575
5576     case 0x15e0:        // (green)
5577       element = EL_CHAR_B;
5578       break;
5579
5580     case 0x15e1:        // (green)
5581       element = EL_CHAR_C;
5582       break;
5583
5584     case 0x15e2:        // (green)
5585       element = EL_CHAR_D;
5586       break;
5587
5588     case 0x15e3:        // (green)
5589       element = EL_CHAR_E;
5590       break;
5591
5592     case 0x15e4:        // (green)
5593       element = EL_CHAR_F;
5594       break;
5595
5596     case 0x15e5:        // (green)
5597       element = EL_CHAR_G;
5598       break;
5599
5600     case 0x15e6:        // (green)
5601       element = EL_CHAR_H;
5602       break;
5603
5604     case 0x15e7:        // (green)
5605       element = EL_CHAR_I;
5606       break;
5607
5608     case 0x15e8:        // (green)
5609       element = EL_CHAR_J;
5610       break;
5611
5612     case 0x15e9:        // (green)
5613       element = EL_CHAR_K;
5614       break;
5615
5616     case 0x15ea:        // (green)
5617       element = EL_CHAR_L;
5618       break;
5619
5620     case 0x15eb:        // (green)
5621       element = EL_CHAR_M;
5622       break;
5623
5624     case 0x15ec:        // (green)
5625       element = EL_CHAR_N;
5626       break;
5627
5628     case 0x15ed:        // (green)
5629       element = EL_CHAR_O;
5630       break;
5631
5632     case 0x15ee:        // (green)
5633       element = EL_CHAR_P;
5634       break;
5635
5636     case 0x15ef:        // (green)
5637       element = EL_CHAR_Q;
5638       break;
5639
5640     case 0x15f0:        // (green)
5641       element = EL_CHAR_R;
5642       break;
5643
5644     case 0x15f1:        // (green)
5645       element = EL_CHAR_S;
5646       break;
5647
5648     case 0x15f2:        // (green)
5649       element = EL_CHAR_T;
5650       break;
5651
5652     case 0x15f3:        // (green)
5653       element = EL_CHAR_U;
5654       break;
5655
5656     case 0x15f4:        // (green)
5657       element = EL_CHAR_V;
5658       break;
5659
5660     case 0x15f5:        // (green)
5661       element = EL_CHAR_W;
5662       break;
5663
5664     case 0x15f6:        // (green)
5665       element = EL_CHAR_X;
5666       break;
5667
5668     case 0x15f7:        // (green)
5669       element = EL_CHAR_Y;
5670       break;
5671
5672     case 0x15f8:        // (green)
5673       element = EL_CHAR_Z;
5674       break;
5675
5676     case 0x15f9:        // (green)
5677       element = EL_CHAR_AUMLAUT;
5678       break;
5679
5680     case 0x15fa:        // (green)
5681       element = EL_CHAR_OUMLAUT;
5682       break;
5683
5684     case 0x15fb:        // (green)
5685       element = EL_CHAR_UUMLAUT;
5686       break;
5687
5688     case 0x15fc:        // (green)
5689       element = EL_CHAR_0;
5690       break;
5691
5692     case 0x15fd:        // (green)
5693       element = EL_CHAR_1;
5694       break;
5695
5696     case 0x15fe:        // (green)
5697       element = EL_CHAR_2;
5698       break;
5699
5700     case 0x15ff:        // (green)
5701       element = EL_CHAR_3;
5702       break;
5703
5704     case 0x1600:        // (green)
5705       element = EL_CHAR_4;
5706       break;
5707
5708     case 0x1601:        // (green)
5709       element = EL_CHAR_5;
5710       break;
5711
5712     case 0x1602:        // (green)
5713       element = EL_CHAR_6;
5714       break;
5715
5716     case 0x1603:        // (green)
5717       element = EL_CHAR_7;
5718       break;
5719
5720     case 0x1604:        // (green)
5721       element = EL_CHAR_8;
5722       break;
5723
5724     case 0x1605:        // (green)
5725       element = EL_CHAR_9;
5726       break;
5727
5728     case 0x1606:        // (green)
5729       element = EL_CHAR_PERIOD;
5730       break;
5731
5732     case 0x1607:        // (green)
5733       element = EL_CHAR_EXCLAM;
5734       break;
5735
5736     case 0x1608:        // (green)
5737       element = EL_CHAR_COLON;
5738       break;
5739
5740     case 0x1609:        // (green)
5741       element = EL_CHAR_LESS;
5742       break;
5743
5744     case 0x160a:        // (green)
5745       element = EL_CHAR_GREATER;
5746       break;
5747
5748     case 0x160b:        // (green)
5749       element = EL_CHAR_QUESTION;
5750       break;
5751
5752     case 0x160c:        // (green)
5753       element = EL_CHAR_COPYRIGHT;
5754       break;
5755
5756     case 0x160d:        // (green)
5757       element = EL_CHAR_UP;
5758       break;
5759
5760     case 0x160e:        // (green)
5761       element = EL_CHAR_DOWN;
5762       break;
5763
5764     case 0x160f:        // (green)
5765       element = EL_CHAR_BUTTON;
5766       break;
5767
5768     case 0x1610:        // (green)
5769       element = EL_CHAR_PLUS;
5770       break;
5771
5772     case 0x1611:        // (green)
5773       element = EL_CHAR_MINUS;
5774       break;
5775
5776     case 0x1612:        // (green)
5777       element = EL_CHAR_APOSTROPHE;
5778       break;
5779
5780     case 0x1613:        // (green)
5781       element = EL_CHAR_PARENLEFT;
5782       break;
5783
5784     case 0x1614:        // (green)
5785       element = EL_CHAR_PARENRIGHT;
5786       break;
5787
5788     case 0x1615:        // (blue steel)
5789       element = EL_STEEL_CHAR_A;
5790       break;
5791
5792     case 0x1616:        // (blue steel)
5793       element = EL_STEEL_CHAR_B;
5794       break;
5795
5796     case 0x1617:        // (blue steel)
5797       element = EL_STEEL_CHAR_C;
5798       break;
5799
5800     case 0x1618:        // (blue steel)
5801       element = EL_STEEL_CHAR_D;
5802       break;
5803
5804     case 0x1619:        // (blue steel)
5805       element = EL_STEEL_CHAR_E;
5806       break;
5807
5808     case 0x161a:        // (blue steel)
5809       element = EL_STEEL_CHAR_F;
5810       break;
5811
5812     case 0x161b:        // (blue steel)
5813       element = EL_STEEL_CHAR_G;
5814       break;
5815
5816     case 0x161c:        // (blue steel)
5817       element = EL_STEEL_CHAR_H;
5818       break;
5819
5820     case 0x161d:        // (blue steel)
5821       element = EL_STEEL_CHAR_I;
5822       break;
5823
5824     case 0x161e:        // (blue steel)
5825       element = EL_STEEL_CHAR_J;
5826       break;
5827
5828     case 0x161f:        // (blue steel)
5829       element = EL_STEEL_CHAR_K;
5830       break;
5831
5832     case 0x1620:        // (blue steel)
5833       element = EL_STEEL_CHAR_L;
5834       break;
5835
5836     case 0x1621:        // (blue steel)
5837       element = EL_STEEL_CHAR_M;
5838       break;
5839
5840     case 0x1622:        // (blue steel)
5841       element = EL_STEEL_CHAR_N;
5842       break;
5843
5844     case 0x1623:        // (blue steel)
5845       element = EL_STEEL_CHAR_O;
5846       break;
5847
5848     case 0x1624:        // (blue steel)
5849       element = EL_STEEL_CHAR_P;
5850       break;
5851
5852     case 0x1625:        // (blue steel)
5853       element = EL_STEEL_CHAR_Q;
5854       break;
5855
5856     case 0x1626:        // (blue steel)
5857       element = EL_STEEL_CHAR_R;
5858       break;
5859
5860     case 0x1627:        // (blue steel)
5861       element = EL_STEEL_CHAR_S;
5862       break;
5863
5864     case 0x1628:        // (blue steel)
5865       element = EL_STEEL_CHAR_T;
5866       break;
5867
5868     case 0x1629:        // (blue steel)
5869       element = EL_STEEL_CHAR_U;
5870       break;
5871
5872     case 0x162a:        // (blue steel)
5873       element = EL_STEEL_CHAR_V;
5874       break;
5875
5876     case 0x162b:        // (blue steel)
5877       element = EL_STEEL_CHAR_W;
5878       break;
5879
5880     case 0x162c:        // (blue steel)
5881       element = EL_STEEL_CHAR_X;
5882       break;
5883
5884     case 0x162d:        // (blue steel)
5885       element = EL_STEEL_CHAR_Y;
5886       break;
5887
5888     case 0x162e:        // (blue steel)
5889       element = EL_STEEL_CHAR_Z;
5890       break;
5891
5892     case 0x162f:        // (blue steel)
5893       element = EL_STEEL_CHAR_AUMLAUT;
5894       break;
5895
5896     case 0x1630:        // (blue steel)
5897       element = EL_STEEL_CHAR_OUMLAUT;
5898       break;
5899
5900     case 0x1631:        // (blue steel)
5901       element = EL_STEEL_CHAR_UUMLAUT;
5902       break;
5903
5904     case 0x1632:        // (blue steel)
5905       element = EL_STEEL_CHAR_0;
5906       break;
5907
5908     case 0x1633:        // (blue steel)
5909       element = EL_STEEL_CHAR_1;
5910       break;
5911
5912     case 0x1634:        // (blue steel)
5913       element = EL_STEEL_CHAR_2;
5914       break;
5915
5916     case 0x1635:        // (blue steel)
5917       element = EL_STEEL_CHAR_3;
5918       break;
5919
5920     case 0x1636:        // (blue steel)
5921       element = EL_STEEL_CHAR_4;
5922       break;
5923
5924     case 0x1637:        // (blue steel)
5925       element = EL_STEEL_CHAR_5;
5926       break;
5927
5928     case 0x1638:        // (blue steel)
5929       element = EL_STEEL_CHAR_6;
5930       break;
5931
5932     case 0x1639:        // (blue steel)
5933       element = EL_STEEL_CHAR_7;
5934       break;
5935
5936     case 0x163a:        // (blue steel)
5937       element = EL_STEEL_CHAR_8;
5938       break;
5939
5940     case 0x163b:        // (blue steel)
5941       element = EL_STEEL_CHAR_9;
5942       break;
5943
5944     case 0x163c:        // (blue steel)
5945       element = EL_STEEL_CHAR_PERIOD;
5946       break;
5947
5948     case 0x163d:        // (blue steel)
5949       element = EL_STEEL_CHAR_EXCLAM;
5950       break;
5951
5952     case 0x163e:        // (blue steel)
5953       element = EL_STEEL_CHAR_COLON;
5954       break;
5955
5956     case 0x163f:        // (blue steel)
5957       element = EL_STEEL_CHAR_LESS;
5958       break;
5959
5960     case 0x1640:        // (blue steel)
5961       element = EL_STEEL_CHAR_GREATER;
5962       break;
5963
5964     case 0x1641:        // (blue steel)
5965       element = EL_STEEL_CHAR_QUESTION;
5966       break;
5967
5968     case 0x1642:        // (blue steel)
5969       element = EL_STEEL_CHAR_COPYRIGHT;
5970       break;
5971
5972     case 0x1643:        // (blue steel)
5973       element = EL_STEEL_CHAR_UP;
5974       break;
5975
5976     case 0x1644:        // (blue steel)
5977       element = EL_STEEL_CHAR_DOWN;
5978       break;
5979
5980     case 0x1645:        // (blue steel)
5981       element = EL_STEEL_CHAR_BUTTON;
5982       break;
5983
5984     case 0x1646:        // (blue steel)
5985       element = EL_STEEL_CHAR_PLUS;
5986       break;
5987
5988     case 0x1647:        // (blue steel)
5989       element = EL_STEEL_CHAR_MINUS;
5990       break;
5991
5992     case 0x1648:        // (blue steel)
5993       element = EL_STEEL_CHAR_APOSTROPHE;
5994       break;
5995
5996     case 0x1649:        // (blue steel)
5997       element = EL_STEEL_CHAR_PARENLEFT;
5998       break;
5999
6000     case 0x164a:        // (blue steel)
6001       element = EL_STEEL_CHAR_PARENRIGHT;
6002       break;
6003
6004     case 0x164b:        // (green steel)
6005       element = EL_STEEL_CHAR_A;
6006       break;
6007
6008     case 0x164c:        // (green steel)
6009       element = EL_STEEL_CHAR_B;
6010       break;
6011
6012     case 0x164d:        // (green steel)
6013       element = EL_STEEL_CHAR_C;
6014       break;
6015
6016     case 0x164e:        // (green steel)
6017       element = EL_STEEL_CHAR_D;
6018       break;
6019
6020     case 0x164f:        // (green steel)
6021       element = EL_STEEL_CHAR_E;
6022       break;
6023
6024     case 0x1650:        // (green steel)
6025       element = EL_STEEL_CHAR_F;
6026       break;
6027
6028     case 0x1651:        // (green steel)
6029       element = EL_STEEL_CHAR_G;
6030       break;
6031
6032     case 0x1652:        // (green steel)
6033       element = EL_STEEL_CHAR_H;
6034       break;
6035
6036     case 0x1653:        // (green steel)
6037       element = EL_STEEL_CHAR_I;
6038       break;
6039
6040     case 0x1654:        // (green steel)
6041       element = EL_STEEL_CHAR_J;
6042       break;
6043
6044     case 0x1655:        // (green steel)
6045       element = EL_STEEL_CHAR_K;
6046       break;
6047
6048     case 0x1656:        // (green steel)
6049       element = EL_STEEL_CHAR_L;
6050       break;
6051
6052     case 0x1657:        // (green steel)
6053       element = EL_STEEL_CHAR_M;
6054       break;
6055
6056     case 0x1658:        // (green steel)
6057       element = EL_STEEL_CHAR_N;
6058       break;
6059
6060     case 0x1659:        // (green steel)
6061       element = EL_STEEL_CHAR_O;
6062       break;
6063
6064     case 0x165a:        // (green steel)
6065       element = EL_STEEL_CHAR_P;
6066       break;
6067
6068     case 0x165b:        // (green steel)
6069       element = EL_STEEL_CHAR_Q;
6070       break;
6071
6072     case 0x165c:        // (green steel)
6073       element = EL_STEEL_CHAR_R;
6074       break;
6075
6076     case 0x165d:        // (green steel)
6077       element = EL_STEEL_CHAR_S;
6078       break;
6079
6080     case 0x165e:        // (green steel)
6081       element = EL_STEEL_CHAR_T;
6082       break;
6083
6084     case 0x165f:        // (green steel)
6085       element = EL_STEEL_CHAR_U;
6086       break;
6087
6088     case 0x1660:        // (green steel)
6089       element = EL_STEEL_CHAR_V;
6090       break;
6091
6092     case 0x1661:        // (green steel)
6093       element = EL_STEEL_CHAR_W;
6094       break;
6095
6096     case 0x1662:        // (green steel)
6097       element = EL_STEEL_CHAR_X;
6098       break;
6099
6100     case 0x1663:        // (green steel)
6101       element = EL_STEEL_CHAR_Y;
6102       break;
6103
6104     case 0x1664:        // (green steel)
6105       element = EL_STEEL_CHAR_Z;
6106       break;
6107
6108     case 0x1665:        // (green steel)
6109       element = EL_STEEL_CHAR_AUMLAUT;
6110       break;
6111
6112     case 0x1666:        // (green steel)
6113       element = EL_STEEL_CHAR_OUMLAUT;
6114       break;
6115
6116     case 0x1667:        // (green steel)
6117       element = EL_STEEL_CHAR_UUMLAUT;
6118       break;
6119
6120     case 0x1668:        // (green steel)
6121       element = EL_STEEL_CHAR_0;
6122       break;
6123
6124     case 0x1669:        // (green steel)
6125       element = EL_STEEL_CHAR_1;
6126       break;
6127
6128     case 0x166a:        // (green steel)
6129       element = EL_STEEL_CHAR_2;
6130       break;
6131
6132     case 0x166b:        // (green steel)
6133       element = EL_STEEL_CHAR_3;
6134       break;
6135
6136     case 0x166c:        // (green steel)
6137       element = EL_STEEL_CHAR_4;
6138       break;
6139
6140     case 0x166d:        // (green steel)
6141       element = EL_STEEL_CHAR_5;
6142       break;
6143
6144     case 0x166e:        // (green steel)
6145       element = EL_STEEL_CHAR_6;
6146       break;
6147
6148     case 0x166f:        // (green steel)
6149       element = EL_STEEL_CHAR_7;
6150       break;
6151
6152     case 0x1670:        // (green steel)
6153       element = EL_STEEL_CHAR_8;
6154       break;
6155
6156     case 0x1671:        // (green steel)
6157       element = EL_STEEL_CHAR_9;
6158       break;
6159
6160     case 0x1672:        // (green steel)
6161       element = EL_STEEL_CHAR_PERIOD;
6162       break;
6163
6164     case 0x1673:        // (green steel)
6165       element = EL_STEEL_CHAR_EXCLAM;
6166       break;
6167
6168     case 0x1674:        // (green steel)
6169       element = EL_STEEL_CHAR_COLON;
6170       break;
6171
6172     case 0x1675:        // (green steel)
6173       element = EL_STEEL_CHAR_LESS;
6174       break;
6175
6176     case 0x1676:        // (green steel)
6177       element = EL_STEEL_CHAR_GREATER;
6178       break;
6179
6180     case 0x1677:        // (green steel)
6181       element = EL_STEEL_CHAR_QUESTION;
6182       break;
6183
6184     case 0x1678:        // (green steel)
6185       element = EL_STEEL_CHAR_COPYRIGHT;
6186       break;
6187
6188     case 0x1679:        // (green steel)
6189       element = EL_STEEL_CHAR_UP;
6190       break;
6191
6192     case 0x167a:        // (green steel)
6193       element = EL_STEEL_CHAR_DOWN;
6194       break;
6195
6196     case 0x167b:        // (green steel)
6197       element = EL_STEEL_CHAR_BUTTON;
6198       break;
6199
6200     case 0x167c:        // (green steel)
6201       element = EL_STEEL_CHAR_PLUS;
6202       break;
6203
6204     case 0x167d:        // (green steel)
6205       element = EL_STEEL_CHAR_MINUS;
6206       break;
6207
6208     case 0x167e:        // (green steel)
6209       element = EL_STEEL_CHAR_APOSTROPHE;
6210       break;
6211
6212     case 0x167f:        // (green steel)
6213       element = EL_STEEL_CHAR_PARENLEFT;
6214       break;
6215
6216     case 0x1680:        // (green steel)
6217       element = EL_STEEL_CHAR_PARENRIGHT;
6218       break;
6219
6220     case 0x1681:        // gate (red)
6221       element = EL_EM_GATE_1;
6222       break;
6223
6224     case 0x1682:        // secret gate (red)
6225       element = EL_EM_GATE_1_GRAY;
6226       break;
6227
6228     case 0x1683:        // gate (yellow)
6229       element = EL_EM_GATE_2;
6230       break;
6231
6232     case 0x1684:        // secret gate (yellow)
6233       element = EL_EM_GATE_2_GRAY;
6234       break;
6235
6236     case 0x1685:        // gate (blue)
6237       element = EL_EM_GATE_4;
6238       break;
6239
6240     case 0x1686:        // secret gate (blue)
6241       element = EL_EM_GATE_4_GRAY;
6242       break;
6243
6244     case 0x1687:        // gate (green)
6245       element = EL_EM_GATE_3;
6246       break;
6247
6248     case 0x1688:        // secret gate (green)
6249       element = EL_EM_GATE_3_GRAY;
6250       break;
6251
6252     case 0x1689:        // gate (white)
6253       element = EL_DC_GATE_WHITE;
6254       break;
6255
6256     case 0x168a:        // secret gate (white)
6257       element = EL_DC_GATE_WHITE_GRAY;
6258       break;
6259
6260     case 0x168b:        // secret gate (no key)
6261       element = EL_DC_GATE_FAKE_GRAY;
6262       break;
6263
6264     case 0x168c:
6265       element = EL_ROBOT_WHEEL;
6266       break;
6267
6268     case 0x168d:
6269       element = EL_DC_TIMEGATE_SWITCH;
6270       break;
6271
6272     case 0x168e:
6273       element = EL_ACID_POOL_BOTTOM;
6274       break;
6275
6276     case 0x168f:
6277       element = EL_ACID_POOL_TOPLEFT;
6278       break;
6279
6280     case 0x1690:
6281       element = EL_ACID_POOL_TOPRIGHT;
6282       break;
6283
6284     case 0x1691:
6285       element = EL_ACID_POOL_BOTTOMLEFT;
6286       break;
6287
6288     case 0x1692:
6289       element = EL_ACID_POOL_BOTTOMRIGHT;
6290       break;
6291
6292     case 0x1693:
6293       element = EL_STEELWALL;
6294       break;
6295
6296     case 0x1694:
6297       element = EL_STEELWALL_SLIPPERY;
6298       break;
6299
6300     case 0x1695:        // steel wall (not round)
6301       element = EL_STEELWALL;
6302       break;
6303
6304     case 0x1696:        // steel wall (left)
6305       element = EL_DC_STEELWALL_1_LEFT;
6306       break;
6307
6308     case 0x1697:        // steel wall (bottom)
6309       element = EL_DC_STEELWALL_1_BOTTOM;
6310       break;
6311
6312     case 0x1698:        // steel wall (right)
6313       element = EL_DC_STEELWALL_1_RIGHT;
6314       break;
6315
6316     case 0x1699:        // steel wall (top)
6317       element = EL_DC_STEELWALL_1_TOP;
6318       break;
6319
6320     case 0x169a:        // steel wall (left/bottom)
6321       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6322       break;
6323
6324     case 0x169b:        // steel wall (right/bottom)
6325       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6326       break;
6327
6328     case 0x169c:        // steel wall (right/top)
6329       element = EL_DC_STEELWALL_1_TOPRIGHT;
6330       break;
6331
6332     case 0x169d:        // steel wall (left/top)
6333       element = EL_DC_STEELWALL_1_TOPLEFT;
6334       break;
6335
6336     case 0x169e:        // steel wall (right/bottom small)
6337       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6338       break;
6339
6340     case 0x169f:        // steel wall (left/bottom small)
6341       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6342       break;
6343
6344     case 0x16a0:        // steel wall (right/top small)
6345       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6346       break;
6347
6348     case 0x16a1:        // steel wall (left/top small)
6349       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6350       break;
6351
6352     case 0x16a2:        // steel wall (left/right)
6353       element = EL_DC_STEELWALL_1_VERTICAL;
6354       break;
6355
6356     case 0x16a3:        // steel wall (top/bottom)
6357       element = EL_DC_STEELWALL_1_HORIZONTAL;
6358       break;
6359
6360     case 0x16a4:        // steel wall 2 (left end)
6361       element = EL_DC_STEELWALL_2_LEFT;
6362       break;
6363
6364     case 0x16a5:        // steel wall 2 (right end)
6365       element = EL_DC_STEELWALL_2_RIGHT;
6366       break;
6367
6368     case 0x16a6:        // steel wall 2 (top end)
6369       element = EL_DC_STEELWALL_2_TOP;
6370       break;
6371
6372     case 0x16a7:        // steel wall 2 (bottom end)
6373       element = EL_DC_STEELWALL_2_BOTTOM;
6374       break;
6375
6376     case 0x16a8:        // steel wall 2 (left/right)
6377       element = EL_DC_STEELWALL_2_HORIZONTAL;
6378       break;
6379
6380     case 0x16a9:        // steel wall 2 (up/down)
6381       element = EL_DC_STEELWALL_2_VERTICAL;
6382       break;
6383
6384     case 0x16aa:        // steel wall 2 (mid)
6385       element = EL_DC_STEELWALL_2_MIDDLE;
6386       break;
6387
6388     case 0x16ab:
6389       element = EL_SIGN_EXCLAMATION;
6390       break;
6391
6392     case 0x16ac:
6393       element = EL_SIGN_RADIOACTIVITY;
6394       break;
6395
6396     case 0x16ad:
6397       element = EL_SIGN_STOP;
6398       break;
6399
6400     case 0x16ae:
6401       element = EL_SIGN_WHEELCHAIR;
6402       break;
6403
6404     case 0x16af:
6405       element = EL_SIGN_PARKING;
6406       break;
6407
6408     case 0x16b0:
6409       element = EL_SIGN_NO_ENTRY;
6410       break;
6411
6412     case 0x16b1:
6413       element = EL_SIGN_HEART;
6414       break;
6415
6416     case 0x16b2:
6417       element = EL_SIGN_GIVE_WAY;
6418       break;
6419
6420     case 0x16b3:
6421       element = EL_SIGN_ENTRY_FORBIDDEN;
6422       break;
6423
6424     case 0x16b4:
6425       element = EL_SIGN_EMERGENCY_EXIT;
6426       break;
6427
6428     case 0x16b5:
6429       element = EL_SIGN_YIN_YANG;
6430       break;
6431
6432     case 0x16b6:
6433       element = EL_WALL_EMERALD;
6434       break;
6435
6436     case 0x16b7:
6437       element = EL_WALL_DIAMOND;
6438       break;
6439
6440     case 0x16b8:
6441       element = EL_WALL_PEARL;
6442       break;
6443
6444     case 0x16b9:
6445       element = EL_WALL_CRYSTAL;
6446       break;
6447
6448     case 0x16ba:
6449       element = EL_INVISIBLE_WALL;
6450       break;
6451
6452     case 0x16bb:
6453       element = EL_INVISIBLE_STEELWALL;
6454       break;
6455
6456       // 0x16bc - 0x16cb:
6457       // EL_INVISIBLE_SAND
6458
6459     case 0x16cc:
6460       element = EL_LIGHT_SWITCH;
6461       break;
6462
6463     case 0x16cd:
6464       element = EL_ENVELOPE_1;
6465       break;
6466
6467     default:
6468       if (element >= 0x0117 && element <= 0x036e)       // (?)
6469         element = EL_DIAMOND;
6470       else if (element >= 0x042d && element <= 0x0684)  // (?)
6471         element = EL_EMERALD;
6472       else if (element >= 0x157c && element <= 0x158b)
6473         element = EL_SAND;
6474       else if (element >= 0x1590 && element <= 0x159f)
6475         element = EL_DC_LANDMINE;
6476       else if (element >= 0x16bc && element <= 0x16cb)
6477         element = EL_INVISIBLE_SAND;
6478       else
6479       {
6480         Warn("unknown Diamond Caves element 0x%04x", element);
6481
6482         element = EL_UNKNOWN;
6483       }
6484       break;
6485   }
6486
6487   return getMappedElement(element);
6488 }
6489
6490 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6491 {
6492   byte header[DC_LEVEL_HEADER_SIZE];
6493   int envelope_size;
6494   int envelope_header_pos = 62;
6495   int envelope_content_pos = 94;
6496   int level_name_pos = 251;
6497   int level_author_pos = 292;
6498   int envelope_header_len;
6499   int envelope_content_len;
6500   int level_name_len;
6501   int level_author_len;
6502   int fieldx, fieldy;
6503   int num_yamyam_contents;
6504   int i, x, y;
6505
6506   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6507
6508   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6509   {
6510     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6511
6512     header[i * 2 + 0] = header_word >> 8;
6513     header[i * 2 + 1] = header_word & 0xff;
6514   }
6515
6516   // read some values from level header to check level decoding integrity
6517   fieldx = header[6] | (header[7] << 8);
6518   fieldy = header[8] | (header[9] << 8);
6519   num_yamyam_contents = header[60] | (header[61] << 8);
6520
6521   // do some simple sanity checks to ensure that level was correctly decoded
6522   if (fieldx < 1 || fieldx > 256 ||
6523       fieldy < 1 || fieldy > 256 ||
6524       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6525   {
6526     level->no_valid_file = TRUE;
6527
6528     Warn("cannot decode level from stream -- using empty level");
6529
6530     return;
6531   }
6532
6533   // maximum envelope header size is 31 bytes
6534   envelope_header_len   = header[envelope_header_pos];
6535   // maximum envelope content size is 110 (156?) bytes
6536   envelope_content_len  = header[envelope_content_pos];
6537
6538   // maximum level title size is 40 bytes
6539   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6540   // maximum level author size is 30 (51?) bytes
6541   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6542
6543   envelope_size = 0;
6544
6545   for (i = 0; i < envelope_header_len; i++)
6546     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6547       level->envelope[0].text[envelope_size++] =
6548         header[envelope_header_pos + 1 + i];
6549
6550   if (envelope_header_len > 0 && envelope_content_len > 0)
6551   {
6552     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6553       level->envelope[0].text[envelope_size++] = '\n';
6554     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6555       level->envelope[0].text[envelope_size++] = '\n';
6556   }
6557
6558   for (i = 0; i < envelope_content_len; i++)
6559     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6560       level->envelope[0].text[envelope_size++] =
6561         header[envelope_content_pos + 1 + i];
6562
6563   level->envelope[0].text[envelope_size] = '\0';
6564
6565   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6566   level->envelope[0].ysize = 10;
6567   level->envelope[0].autowrap = TRUE;
6568   level->envelope[0].centered = TRUE;
6569
6570   for (i = 0; i < level_name_len; i++)
6571     level->name[i] = header[level_name_pos + 1 + i];
6572   level->name[level_name_len] = '\0';
6573
6574   for (i = 0; i < level_author_len; i++)
6575     level->author[i] = header[level_author_pos + 1 + i];
6576   level->author[level_author_len] = '\0';
6577
6578   num_yamyam_contents = header[60] | (header[61] << 8);
6579   level->num_yamyam_contents =
6580     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6581
6582   for (i = 0; i < num_yamyam_contents; i++)
6583   {
6584     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6585     {
6586       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6587       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6588
6589       if (i < MAX_ELEMENT_CONTENTS)
6590         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6591     }
6592   }
6593
6594   fieldx = header[6] | (header[7] << 8);
6595   fieldy = header[8] | (header[9] << 8);
6596   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6597   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6598
6599   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6600   {
6601     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6602     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6603
6604     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6605       level->field[x][y] = getMappedElement_DC(element_dc);
6606   }
6607
6608   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6609   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6610   level->field[x][y] = EL_PLAYER_1;
6611
6612   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6613   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6614   level->field[x][y] = EL_PLAYER_2;
6615
6616   level->gems_needed            = header[18] | (header[19] << 8);
6617
6618   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6619   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6620   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6621   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6622   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6623   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6624   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6625   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6626   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6627   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6628   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6629   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6630
6631   level->time                   = header[44] | (header[45] << 8);
6632
6633   level->amoeba_speed           = header[46] | (header[47] << 8);
6634   level->time_light             = header[48] | (header[49] << 8);
6635   level->time_timegate          = header[50] | (header[51] << 8);
6636   level->time_wheel             = header[52] | (header[53] << 8);
6637   level->time_magic_wall        = header[54] | (header[55] << 8);
6638   level->extra_time             = header[56] | (header[57] << 8);
6639   level->shield_normal_time     = header[58] | (header[59] << 8);
6640
6641   // shield and extra time elements do not have a score
6642   level->score[SC_SHIELD]       = 0;
6643   level->extra_time_score       = 0;
6644
6645   // set time for normal and deadly shields to the same value
6646   level->shield_deadly_time     = level->shield_normal_time;
6647
6648   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6649   // can slip down from flat walls, like normal walls and steel walls
6650   level->em_slippery_gems = TRUE;
6651
6652   // time score is counted for each 10 seconds left in Diamond Caves levels
6653   level->time_score_base = 10;
6654 }
6655
6656 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6657                                      struct LevelFileInfo *level_file_info,
6658                                      boolean level_info_only)
6659 {
6660   char *filename = level_file_info->filename;
6661   File *file;
6662   int num_magic_bytes = 8;
6663   char magic_bytes[num_magic_bytes + 1];
6664   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6665
6666   if (!(file = openFile(filename, MODE_READ)))
6667   {
6668     level->no_valid_file = TRUE;
6669
6670     if (!level_info_only)
6671       Warn("cannot read level '%s' -- using empty level", filename);
6672
6673     return;
6674   }
6675
6676   // fseek(file, 0x0000, SEEK_SET);
6677
6678   if (level_file_info->packed)
6679   {
6680     // read "magic bytes" from start of file
6681     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6682       magic_bytes[0] = '\0';
6683
6684     // check "magic bytes" for correct file format
6685     if (!strPrefix(magic_bytes, "DC2"))
6686     {
6687       level->no_valid_file = TRUE;
6688
6689       Warn("unknown DC level file '%s' -- using empty level", filename);
6690
6691       return;
6692     }
6693
6694     if (strPrefix(magic_bytes, "DC2Win95") ||
6695         strPrefix(magic_bytes, "DC2Win98"))
6696     {
6697       int position_first_level = 0x00fa;
6698       int extra_bytes = 4;
6699       int skip_bytes;
6700
6701       // advance file stream to first level inside the level package
6702       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6703
6704       // each block of level data is followed by block of non-level data
6705       num_levels_to_skip *= 2;
6706
6707       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6708       while (num_levels_to_skip >= 0)
6709       {
6710         // advance file stream to next level inside the level package
6711         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6712         {
6713           level->no_valid_file = TRUE;
6714
6715           Warn("cannot fseek in file '%s' -- using empty level", filename);
6716
6717           return;
6718         }
6719
6720         // skip apparently unused extra bytes following each level
6721         ReadUnusedBytesFromFile(file, extra_bytes);
6722
6723         // read size of next level in level package
6724         skip_bytes = getFile32BitLE(file);
6725
6726         num_levels_to_skip--;
6727       }
6728     }
6729     else
6730     {
6731       level->no_valid_file = TRUE;
6732
6733       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6734
6735       return;
6736     }
6737   }
6738
6739   LoadLevelFromFileStream_DC(file, level);
6740
6741   closeFile(file);
6742 }
6743
6744
6745 // ----------------------------------------------------------------------------
6746 // functions for loading SB level
6747 // ----------------------------------------------------------------------------
6748
6749 int getMappedElement_SB(int element_ascii, boolean use_ces)
6750 {
6751   static struct
6752   {
6753     int ascii;
6754     int sb;
6755     int ce;
6756   }
6757   sb_element_mapping[] =
6758   {
6759     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6760     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6761     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6762     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6763     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6764     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6765     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6766     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6767
6768     { 0,   -1,                      -1          },
6769   };
6770
6771   int i;
6772
6773   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6774     if (element_ascii == sb_element_mapping[i].ascii)
6775       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6776
6777   return EL_UNDEFINED;
6778 }
6779
6780 static void SetLevelSettings_SB(struct LevelInfo *level)
6781 {
6782   // time settings
6783   level->time = 0;
6784   level->use_step_counter = TRUE;
6785
6786   // score settings
6787   level->score[SC_TIME_BONUS] = 0;
6788   level->time_score_base = 1;
6789   level->rate_time_over_score = TRUE;
6790
6791   // game settings
6792   level->auto_exit_sokoban = TRUE;
6793 }
6794
6795 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6796                                      struct LevelFileInfo *level_file_info,
6797                                      boolean level_info_only)
6798 {
6799   char *filename = level_file_info->filename;
6800   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6801   char last_comment[MAX_LINE_LEN];
6802   char level_name[MAX_LINE_LEN];
6803   char *line_ptr;
6804   File *file;
6805   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6806   boolean read_continued_line = FALSE;
6807   boolean reading_playfield = FALSE;
6808   boolean got_valid_playfield_line = FALSE;
6809   boolean invalid_playfield_char = FALSE;
6810   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6811   int file_level_nr = 0;
6812   int x = 0, y = 0;             // initialized to make compilers happy
6813
6814   last_comment[0] = '\0';
6815   level_name[0] = '\0';
6816
6817   if (!(file = openFile(filename, MODE_READ)))
6818   {
6819     level->no_valid_file = TRUE;
6820
6821     if (!level_info_only)
6822       Warn("cannot read level '%s' -- using empty level", filename);
6823
6824     return;
6825   }
6826
6827   while (!checkEndOfFile(file))
6828   {
6829     // level successfully read, but next level may follow here
6830     if (!got_valid_playfield_line && reading_playfield)
6831     {
6832       // read playfield from single level file -- skip remaining file
6833       if (!level_file_info->packed)
6834         break;
6835
6836       if (file_level_nr >= num_levels_to_skip)
6837         break;
6838
6839       file_level_nr++;
6840
6841       last_comment[0] = '\0';
6842       level_name[0] = '\0';
6843
6844       reading_playfield = FALSE;
6845     }
6846
6847     got_valid_playfield_line = FALSE;
6848
6849     // read next line of input file
6850     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6851       break;
6852
6853     // cut trailing line break (this can be newline and/or carriage return)
6854     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6855       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6856         *line_ptr = '\0';
6857
6858     // copy raw input line for later use (mainly debugging output)
6859     strcpy(line_raw, line);
6860
6861     if (read_continued_line)
6862     {
6863       // append new line to existing line, if there is enough space
6864       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6865         strcat(previous_line, line_ptr);
6866
6867       strcpy(line, previous_line);      // copy storage buffer to line
6868
6869       read_continued_line = FALSE;
6870     }
6871
6872     // if the last character is '\', continue at next line
6873     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6874     {
6875       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6876       strcpy(previous_line, line);      // copy line to storage buffer
6877
6878       read_continued_line = TRUE;
6879
6880       continue;
6881     }
6882
6883     // skip empty lines
6884     if (line[0] == '\0')
6885       continue;
6886
6887     // extract comment text from comment line
6888     if (line[0] == ';')
6889     {
6890       for (line_ptr = line; *line_ptr; line_ptr++)
6891         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6892           break;
6893
6894       strcpy(last_comment, line_ptr);
6895
6896       continue;
6897     }
6898
6899     // extract level title text from line containing level title
6900     if (line[0] == '\'')
6901     {
6902       strcpy(level_name, &line[1]);
6903
6904       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6905         level_name[strlen(level_name) - 1] = '\0';
6906
6907       continue;
6908     }
6909
6910     // skip lines containing only spaces (or empty lines)
6911     for (line_ptr = line; *line_ptr; line_ptr++)
6912       if (*line_ptr != ' ')
6913         break;
6914     if (*line_ptr == '\0')
6915       continue;
6916
6917     // at this point, we have found a line containing part of a playfield
6918
6919     got_valid_playfield_line = TRUE;
6920
6921     if (!reading_playfield)
6922     {
6923       reading_playfield = TRUE;
6924       invalid_playfield_char = FALSE;
6925
6926       for (x = 0; x < MAX_LEV_FIELDX; x++)
6927         for (y = 0; y < MAX_LEV_FIELDY; y++)
6928           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6929
6930       level->fieldx = 0;
6931       level->fieldy = 0;
6932
6933       // start with topmost tile row
6934       y = 0;
6935     }
6936
6937     // skip playfield line if larger row than allowed
6938     if (y >= MAX_LEV_FIELDY)
6939       continue;
6940
6941     // start with leftmost tile column
6942     x = 0;
6943
6944     // read playfield elements from line
6945     for (line_ptr = line; *line_ptr; line_ptr++)
6946     {
6947       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6948
6949       // stop parsing playfield line if larger column than allowed
6950       if (x >= MAX_LEV_FIELDX)
6951         break;
6952
6953       if (mapped_sb_element == EL_UNDEFINED)
6954       {
6955         invalid_playfield_char = TRUE;
6956
6957         break;
6958       }
6959
6960       level->field[x][y] = mapped_sb_element;
6961
6962       // continue with next tile column
6963       x++;
6964
6965       level->fieldx = MAX(x, level->fieldx);
6966     }
6967
6968     if (invalid_playfield_char)
6969     {
6970       // if first playfield line, treat invalid lines as comment lines
6971       if (y == 0)
6972         reading_playfield = FALSE;
6973
6974       continue;
6975     }
6976
6977     // continue with next tile row
6978     y++;
6979   }
6980
6981   closeFile(file);
6982
6983   level->fieldy = y;
6984
6985   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
6986   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
6987
6988   if (!reading_playfield)
6989   {
6990     level->no_valid_file = TRUE;
6991
6992     Warn("cannot read level '%s' -- using empty level", filename);
6993
6994     return;
6995   }
6996
6997   if (*level_name != '\0')
6998   {
6999     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7000     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7001   }
7002   else if (*last_comment != '\0')
7003   {
7004     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7005     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7006   }
7007   else
7008   {
7009     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7010   }
7011
7012   // set all empty fields beyond the border walls to invisible steel wall
7013   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7014   {
7015     if ((x == 0 || x == level->fieldx - 1 ||
7016          y == 0 || y == level->fieldy - 1) &&
7017         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7018       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7019                      level->field, level->fieldx, level->fieldy);
7020   }
7021
7022   // set special level settings for Sokoban levels
7023   SetLevelSettings_SB(level);
7024
7025   if (load_xsb_to_ces)
7026   {
7027     // special global settings can now be set in level template
7028     level->use_custom_template = TRUE;
7029   }
7030 }
7031
7032
7033 // -------------------------------------------------------------------------
7034 // functions for handling native levels
7035 // -------------------------------------------------------------------------
7036
7037 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7038                                      struct LevelFileInfo *level_file_info,
7039                                      boolean level_info_only)
7040 {
7041   int pos = 0;
7042
7043   // determine position of requested level inside level package
7044   if (level_file_info->packed)
7045     pos = level_file_info->nr - leveldir_current->first_level;
7046
7047   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7048     level->no_valid_file = TRUE;
7049 }
7050
7051 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7052                                      struct LevelFileInfo *level_file_info,
7053                                      boolean level_info_only)
7054 {
7055   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7056     level->no_valid_file = TRUE;
7057 }
7058
7059 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7060                                      struct LevelFileInfo *level_file_info,
7061                                      boolean level_info_only)
7062 {
7063   int pos = 0;
7064
7065   // determine position of requested level inside level package
7066   if (level_file_info->packed)
7067     pos = level_file_info->nr - leveldir_current->first_level;
7068
7069   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7070     level->no_valid_file = TRUE;
7071 }
7072
7073 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7074                                      struct LevelFileInfo *level_file_info,
7075                                      boolean level_info_only)
7076 {
7077   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7078     level->no_valid_file = TRUE;
7079 }
7080
7081 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7082 {
7083   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7084     CopyNativeLevel_RND_to_BD(level);
7085   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7086     CopyNativeLevel_RND_to_EM(level);
7087   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7088     CopyNativeLevel_RND_to_SP(level);
7089   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7090     CopyNativeLevel_RND_to_MM(level);
7091 }
7092
7093 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7094 {
7095   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7096     CopyNativeLevel_BD_to_RND(level);
7097   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7098     CopyNativeLevel_EM_to_RND(level);
7099   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7100     CopyNativeLevel_SP_to_RND(level);
7101   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7102     CopyNativeLevel_MM_to_RND(level);
7103 }
7104
7105 void SaveNativeLevel(struct LevelInfo *level)
7106 {
7107   // saving native level files only supported for some game engines
7108   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7109       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7110     return;
7111
7112   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7113                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7114   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7115   char *filename = getLevelFilenameFromBasename(basename);
7116
7117   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7118     return;
7119
7120   boolean success = FALSE;
7121
7122   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7123   {
7124     CopyNativeLevel_RND_to_BD(level);
7125     // CopyNativeTape_RND_to_BD(level);
7126
7127     success = SaveNativeLevel_BD(filename);
7128   }
7129   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7130   {
7131     CopyNativeLevel_RND_to_SP(level);
7132     CopyNativeTape_RND_to_SP(level);
7133
7134     success = SaveNativeLevel_SP(filename);
7135   }
7136
7137   if (success)
7138     Request("Native level file saved!", REQ_CONFIRM);
7139   else
7140     Request("Failed to save native level file!", REQ_CONFIRM);
7141 }
7142
7143
7144 // ----------------------------------------------------------------------------
7145 // functions for loading generic level
7146 // ----------------------------------------------------------------------------
7147
7148 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7149                                   struct LevelFileInfo *level_file_info,
7150                                   boolean level_info_only)
7151 {
7152   // always start with reliable default values
7153   setLevelInfoToDefaults(level, level_info_only, TRUE);
7154
7155   switch (level_file_info->type)
7156   {
7157     case LEVEL_FILE_TYPE_RND:
7158       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7159       break;
7160
7161     case LEVEL_FILE_TYPE_BD:
7162       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7163       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7164       break;
7165
7166     case LEVEL_FILE_TYPE_EM:
7167       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7168       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7169       break;
7170
7171     case LEVEL_FILE_TYPE_SP:
7172       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7173       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7174       break;
7175
7176     case LEVEL_FILE_TYPE_MM:
7177       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7178       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7179       break;
7180
7181     case LEVEL_FILE_TYPE_DC:
7182       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7183       break;
7184
7185     case LEVEL_FILE_TYPE_SB:
7186       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7187       break;
7188
7189     default:
7190       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7191       break;
7192   }
7193
7194   // if level file is invalid, restore level structure to default values
7195   if (level->no_valid_file)
7196     setLevelInfoToDefaults(level, level_info_only, FALSE);
7197
7198   if (check_special_flags("use_native_bd_game_engine"))
7199     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7200
7201   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7202     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7203
7204   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7205     CopyNativeLevel_Native_to_RND(level);
7206 }
7207
7208 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7209 {
7210   static struct LevelFileInfo level_file_info;
7211
7212   // always start with reliable default values
7213   setFileInfoToDefaults(&level_file_info);
7214
7215   level_file_info.nr = 0;                       // unknown level number
7216   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7217
7218   setString(&level_file_info.filename, filename);
7219
7220   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7221 }
7222
7223 static void LoadLevel_InitVersion(struct LevelInfo *level)
7224 {
7225   int i, j;
7226
7227   if (leveldir_current == NULL)         // only when dumping level
7228     return;
7229
7230   // all engine modifications also valid for levels which use latest engine
7231   if (level->game_version < VERSION_IDENT(3,2,0,5))
7232   {
7233     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7234     level->time_score_base = 10;
7235   }
7236
7237   if (leveldir_current->latest_engine)
7238   {
7239     // ---------- use latest game engine --------------------------------------
7240
7241     /* For all levels which are forced to use the latest game engine version
7242        (normally all but user contributed, private and undefined levels), set
7243        the game engine version to the actual version; this allows for actual
7244        corrections in the game engine to take effect for existing, converted
7245        levels (from "classic" or other existing games) to make the emulation
7246        of the corresponding game more accurate, while (hopefully) not breaking
7247        existing levels created from other players. */
7248
7249     level->game_version = GAME_VERSION_ACTUAL;
7250
7251     /* Set special EM style gems behaviour: EM style gems slip down from
7252        normal, steel and growing wall. As this is a more fundamental change,
7253        it seems better to set the default behaviour to "off" (as it is more
7254        natural) and make it configurable in the level editor (as a property
7255        of gem style elements). Already existing converted levels (neither
7256        private nor contributed levels) are changed to the new behaviour. */
7257
7258     if (level->file_version < FILE_VERSION_2_0)
7259       level->em_slippery_gems = TRUE;
7260
7261     return;
7262   }
7263
7264   // ---------- use game engine the level was created with --------------------
7265
7266   /* For all levels which are not forced to use the latest game engine
7267      version (normally user contributed, private and undefined levels),
7268      use the version of the game engine the levels were created for.
7269
7270      Since 2.0.1, the game engine version is now directly stored
7271      in the level file (chunk "VERS"), so there is no need anymore
7272      to set the game version from the file version (except for old,
7273      pre-2.0 levels, where the game version is still taken from the
7274      file format version used to store the level -- see above). */
7275
7276   // player was faster than enemies in 1.0.0 and before
7277   if (level->file_version == FILE_VERSION_1_0)
7278     for (i = 0; i < MAX_PLAYERS; i++)
7279       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7280
7281   // default behaviour for EM style gems was "slippery" only in 2.0.1
7282   if (level->game_version == VERSION_IDENT(2,0,1,0))
7283     level->em_slippery_gems = TRUE;
7284
7285   // springs could be pushed over pits before (pre-release version) 2.2.0
7286   if (level->game_version < VERSION_IDENT(2,2,0,0))
7287     level->use_spring_bug = TRUE;
7288
7289   if (level->game_version < VERSION_IDENT(3,2,0,5))
7290   {
7291     // time orb caused limited time in endless time levels before 3.2.0-5
7292     level->use_time_orb_bug = TRUE;
7293
7294     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7295     level->block_snap_field = FALSE;
7296
7297     // extra time score was same value as time left score before 3.2.0-5
7298     level->extra_time_score = level->score[SC_TIME_BONUS];
7299   }
7300
7301   if (level->game_version < VERSION_IDENT(3,2,0,7))
7302   {
7303     // default behaviour for snapping was "not continuous" before 3.2.0-7
7304     level->continuous_snapping = FALSE;
7305   }
7306
7307   // only few elements were able to actively move into acid before 3.1.0
7308   // trigger settings did not exist before 3.1.0; set to default "any"
7309   if (level->game_version < VERSION_IDENT(3,1,0,0))
7310   {
7311     // correct "can move into acid" settings (all zero in old levels)
7312
7313     level->can_move_into_acid_bits = 0; // nothing can move into acid
7314     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7315
7316     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7317     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7318     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7319     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7320
7321     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7322       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7323
7324     // correct trigger settings (stored as zero == "none" in old levels)
7325
7326     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7327     {
7328       int element = EL_CUSTOM_START + i;
7329       struct ElementInfo *ei = &element_info[element];
7330
7331       for (j = 0; j < ei->num_change_pages; j++)
7332       {
7333         struct ElementChangeInfo *change = &ei->change_page[j];
7334
7335         change->trigger_player = CH_PLAYER_ANY;
7336         change->trigger_page = CH_PAGE_ANY;
7337       }
7338     }
7339   }
7340
7341   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7342   {
7343     int element = EL_CUSTOM_256;
7344     struct ElementInfo *ei = &element_info[element];
7345     struct ElementChangeInfo *change = &ei->change_page[0];
7346
7347     /* This is needed to fix a problem that was caused by a bugfix in function
7348        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7349        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7350        not replace walkable elements, but instead just placed the player on it,
7351        without placing the Sokoban field under the player). Unfortunately, this
7352        breaks "Snake Bite" style levels when the snake is halfway through a door
7353        that just closes (the snake head is still alive and can be moved in this
7354        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7355        player (without Sokoban element) which then gets killed as designed). */
7356
7357     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7358          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7359         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7360       change->target_element = EL_PLAYER_1;
7361   }
7362
7363   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7364   if (level->game_version < VERSION_IDENT(3,2,5,0))
7365   {
7366     /* This is needed to fix a problem that was caused by a bugfix in function
7367        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7368        corrects the behaviour when a custom element changes to another custom
7369        element with a higher element number that has change actions defined.
7370        Normally, only one change per frame is allowed for custom elements.
7371        Therefore, it is checked if a custom element already changed in the
7372        current frame; if it did, subsequent changes are suppressed.
7373        Unfortunately, this is only checked for element changes, but not for
7374        change actions, which are still executed. As the function above loops
7375        through all custom elements from lower to higher, an element change
7376        resulting in a lower CE number won't be checked again, while a target
7377        element with a higher number will also be checked, and potential change
7378        actions will get executed for this CE, too (which is wrong), while
7379        further changes are ignored (which is correct). As this bugfix breaks
7380        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7381        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7382        behaviour for existing levels and tapes that make use of this bug */
7383
7384     level->use_action_after_change_bug = TRUE;
7385   }
7386
7387   // not centering level after relocating player was default only in 3.2.3
7388   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7389     level->shifted_relocation = TRUE;
7390
7391   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7392   if (level->game_version < VERSION_IDENT(3,2,6,0))
7393     level->em_explodes_by_fire = TRUE;
7394
7395   // levels were solved by the first player entering an exit up to 4.1.0.0
7396   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7397     level->solved_by_one_player = TRUE;
7398
7399   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7400   if (level->game_version < VERSION_IDENT(4,1,1,1))
7401     level->use_life_bugs = TRUE;
7402
7403   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7404   if (level->game_version < VERSION_IDENT(4,1,1,1))
7405     level->sb_objects_needed = FALSE;
7406
7407   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7408   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7409     level->finish_dig_collect = FALSE;
7410
7411   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7412   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7413     level->keep_walkable_ce = TRUE;
7414 }
7415
7416 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7417 {
7418   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7419   int x, y;
7420
7421   // check if this level is (not) a Sokoban level
7422   for (y = 0; y < level->fieldy; y++)
7423     for (x = 0; x < level->fieldx; x++)
7424       if (!IS_SB_ELEMENT(Tile[x][y]))
7425         is_sokoban_level = FALSE;
7426
7427   if (is_sokoban_level)
7428   {
7429     // set special level settings for Sokoban levels
7430     SetLevelSettings_SB(level);
7431   }
7432 }
7433
7434 static void LoadLevel_InitSettings(struct LevelInfo *level)
7435 {
7436   // adjust level settings for (non-native) Sokoban-style levels
7437   LoadLevel_InitSettings_SB(level);
7438
7439   // rename levels with title "nameless level" or if renaming is forced
7440   if (leveldir_current->empty_level_name != NULL &&
7441       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7442        leveldir_current->force_level_name))
7443     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7444              leveldir_current->empty_level_name, level_nr);
7445 }
7446
7447 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7448 {
7449   int i, x, y;
7450
7451   // map elements that have changed in newer versions
7452   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7453                                                     level->game_version);
7454   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7455     for (x = 0; x < 3; x++)
7456       for (y = 0; y < 3; y++)
7457         level->yamyam_content[i].e[x][y] =
7458           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7459                                     level->game_version);
7460
7461 }
7462
7463 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7464 {
7465   int i, j;
7466
7467   // map custom element change events that have changed in newer versions
7468   // (these following values were accidentally changed in version 3.0.1)
7469   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7470   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7471   {
7472     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7473     {
7474       int element = EL_CUSTOM_START + i;
7475
7476       // order of checking and copying events to be mapped is important
7477       // (do not change the start and end value -- they are constant)
7478       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7479       {
7480         if (HAS_CHANGE_EVENT(element, j - 2))
7481         {
7482           SET_CHANGE_EVENT(element, j - 2, FALSE);
7483           SET_CHANGE_EVENT(element, j, TRUE);
7484         }
7485       }
7486
7487       // order of checking and copying events to be mapped is important
7488       // (do not change the start and end value -- they are constant)
7489       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7490       {
7491         if (HAS_CHANGE_EVENT(element, j - 1))
7492         {
7493           SET_CHANGE_EVENT(element, j - 1, FALSE);
7494           SET_CHANGE_EVENT(element, j, TRUE);
7495         }
7496       }
7497     }
7498   }
7499
7500   // initialize "can_change" field for old levels with only one change page
7501   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7502   {
7503     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7504     {
7505       int element = EL_CUSTOM_START + i;
7506
7507       if (CAN_CHANGE(element))
7508         element_info[element].change->can_change = TRUE;
7509     }
7510   }
7511
7512   // correct custom element values (for old levels without these options)
7513   if (level->game_version < VERSION_IDENT(3,1,1,0))
7514   {
7515     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7516     {
7517       int element = EL_CUSTOM_START + i;
7518       struct ElementInfo *ei = &element_info[element];
7519
7520       if (ei->access_direction == MV_NO_DIRECTION)
7521         ei->access_direction = MV_ALL_DIRECTIONS;
7522     }
7523   }
7524
7525   // correct custom element values (fix invalid values for all versions)
7526   if (1)
7527   {
7528     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7529     {
7530       int element = EL_CUSTOM_START + i;
7531       struct ElementInfo *ei = &element_info[element];
7532
7533       for (j = 0; j < ei->num_change_pages; j++)
7534       {
7535         struct ElementChangeInfo *change = &ei->change_page[j];
7536
7537         if (change->trigger_player == CH_PLAYER_NONE)
7538           change->trigger_player = CH_PLAYER_ANY;
7539
7540         if (change->trigger_side == CH_SIDE_NONE)
7541           change->trigger_side = CH_SIDE_ANY;
7542       }
7543     }
7544   }
7545
7546   // initialize "can_explode" field for old levels which did not store this
7547   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7548   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7549   {
7550     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7551     {
7552       int element = EL_CUSTOM_START + i;
7553
7554       if (EXPLODES_1X1_OLD(element))
7555         element_info[element].explosion_type = EXPLODES_1X1;
7556
7557       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7558                                              EXPLODES_SMASHED(element) ||
7559                                              EXPLODES_IMPACT(element)));
7560     }
7561   }
7562
7563   // correct previously hard-coded move delay values for maze runner style
7564   if (level->game_version < VERSION_IDENT(3,1,1,0))
7565   {
7566     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7567     {
7568       int element = EL_CUSTOM_START + i;
7569
7570       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7571       {
7572         // previously hard-coded and therefore ignored
7573         element_info[element].move_delay_fixed = 9;
7574         element_info[element].move_delay_random = 0;
7575       }
7576     }
7577   }
7578
7579   // set some other uninitialized values of custom elements in older levels
7580   if (level->game_version < VERSION_IDENT(3,1,0,0))
7581   {
7582     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7583     {
7584       int element = EL_CUSTOM_START + i;
7585
7586       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7587
7588       element_info[element].explosion_delay = 17;
7589       element_info[element].ignition_delay = 8;
7590     }
7591   }
7592
7593   // set mouse click change events to work for left/middle/right mouse button
7594   if (level->game_version < VERSION_IDENT(4,2,3,0))
7595   {
7596     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7597     {
7598       int element = EL_CUSTOM_START + i;
7599       struct ElementInfo *ei = &element_info[element];
7600
7601       for (j = 0; j < ei->num_change_pages; j++)
7602       {
7603         struct ElementChangeInfo *change = &ei->change_page[j];
7604
7605         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7606             change->has_event[CE_PRESSED_BY_MOUSE] ||
7607             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7608             change->has_event[CE_MOUSE_PRESSED_ON_X])
7609           change->trigger_side = CH_SIDE_ANY;
7610       }
7611     }
7612   }
7613 }
7614
7615 static void LoadLevel_InitElements(struct LevelInfo *level)
7616 {
7617   LoadLevel_InitStandardElements(level);
7618
7619   if (level->file_has_custom_elements)
7620     LoadLevel_InitCustomElements(level);
7621
7622   // initialize element properties for level editor etc.
7623   InitElementPropertiesEngine(level->game_version);
7624   InitElementPropertiesGfxElement();
7625 }
7626
7627 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7628 {
7629   int x, y;
7630
7631   // map elements that have changed in newer versions
7632   for (y = 0; y < level->fieldy; y++)
7633     for (x = 0; x < level->fieldx; x++)
7634       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7635                                                      level->game_version);
7636
7637   // clear unused playfield data (nicer if level gets resized in editor)
7638   for (x = 0; x < MAX_LEV_FIELDX; x++)
7639     for (y = 0; y < MAX_LEV_FIELDY; y++)
7640       if (x >= level->fieldx || y >= level->fieldy)
7641         level->field[x][y] = EL_EMPTY;
7642
7643   // copy elements to runtime playfield array
7644   for (x = 0; x < MAX_LEV_FIELDX; x++)
7645     for (y = 0; y < MAX_LEV_FIELDY; y++)
7646       Tile[x][y] = level->field[x][y];
7647
7648   // initialize level size variables for faster access
7649   lev_fieldx = level->fieldx;
7650   lev_fieldy = level->fieldy;
7651
7652   // determine border element for this level
7653   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7654     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7655   else
7656     SetBorderElement();
7657 }
7658
7659 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7660 {
7661   struct LevelFileInfo *level_file_info = &level->file_info;
7662
7663   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7664     CopyNativeLevel_RND_to_Native(level);
7665 }
7666
7667 static void LoadLevelTemplate_LoadAndInit(void)
7668 {
7669   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7670
7671   LoadLevel_InitVersion(&level_template);
7672   LoadLevel_InitElements(&level_template);
7673   LoadLevel_InitSettings(&level_template);
7674
7675   ActivateLevelTemplate();
7676 }
7677
7678 void LoadLevelTemplate(int nr)
7679 {
7680   if (!fileExists(getGlobalLevelTemplateFilename()))
7681   {
7682     Warn("no level template found for this level");
7683
7684     return;
7685   }
7686
7687   setLevelFileInfo(&level_template.file_info, nr);
7688
7689   LoadLevelTemplate_LoadAndInit();
7690 }
7691
7692 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7693 {
7694   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7695
7696   LoadLevelTemplate_LoadAndInit();
7697 }
7698
7699 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7700 {
7701   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7702
7703   if (level.use_custom_template)
7704   {
7705     if (network_level != NULL)
7706       LoadNetworkLevelTemplate(network_level);
7707     else
7708       LoadLevelTemplate(-1);
7709   }
7710
7711   LoadLevel_InitVersion(&level);
7712   LoadLevel_InitElements(&level);
7713   LoadLevel_InitPlayfield(&level);
7714   LoadLevel_InitSettings(&level);
7715
7716   LoadLevel_InitNativeEngines(&level);
7717 }
7718
7719 void LoadLevel(int nr)
7720 {
7721   SetLevelSetInfo(leveldir_current->identifier, nr);
7722
7723   setLevelFileInfo(&level.file_info, nr);
7724
7725   LoadLevel_LoadAndInit(NULL);
7726 }
7727
7728 void LoadLevelInfoOnly(int nr)
7729 {
7730   setLevelFileInfo(&level.file_info, nr);
7731
7732   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7733 }
7734
7735 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7736 {
7737   SetLevelSetInfo(network_level->leveldir_identifier,
7738                   network_level->file_info.nr);
7739
7740   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7741
7742   LoadLevel_LoadAndInit(network_level);
7743 }
7744
7745 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7746 {
7747   int chunk_size = 0;
7748
7749   chunk_size += putFileVersion(file, level->file_version);
7750   chunk_size += putFileVersion(file, level->game_version);
7751
7752   return chunk_size;
7753 }
7754
7755 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7756 {
7757   int chunk_size = 0;
7758
7759   chunk_size += putFile16BitBE(file, level->creation_date.year);
7760   chunk_size += putFile8Bit(file,    level->creation_date.month);
7761   chunk_size += putFile8Bit(file,    level->creation_date.day);
7762
7763   return chunk_size;
7764 }
7765
7766 #if ENABLE_HISTORIC_CHUNKS
7767 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7768 {
7769   int i, x, y;
7770
7771   putFile8Bit(file, level->fieldx);
7772   putFile8Bit(file, level->fieldy);
7773
7774   putFile16BitBE(file, level->time);
7775   putFile16BitBE(file, level->gems_needed);
7776
7777   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7778     putFile8Bit(file, level->name[i]);
7779
7780   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7781     putFile8Bit(file, level->score[i]);
7782
7783   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7784     for (y = 0; y < 3; y++)
7785       for (x = 0; x < 3; x++)
7786         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7787                            level->yamyam_content[i].e[x][y]));
7788   putFile8Bit(file, level->amoeba_speed);
7789   putFile8Bit(file, level->time_magic_wall);
7790   putFile8Bit(file, level->time_wheel);
7791   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7792                      level->amoeba_content));
7793   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7794   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7795   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7796   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7797
7798   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7799
7800   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7801   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7802   putFile32BitBE(file, level->can_move_into_acid_bits);
7803   putFile8Bit(file, level->dont_collide_with_bits);
7804
7805   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7806   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7807
7808   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7809   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7810   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7811
7812   putFile8Bit(file, level->game_engine_type);
7813
7814   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7815 }
7816 #endif
7817
7818 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7819 {
7820   int chunk_size = 0;
7821   int i;
7822
7823   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7824     chunk_size += putFile8Bit(file, level->name[i]);
7825
7826   return chunk_size;
7827 }
7828
7829 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7830 {
7831   int chunk_size = 0;
7832   int i;
7833
7834   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7835     chunk_size += putFile8Bit(file, level->author[i]);
7836
7837   return chunk_size;
7838 }
7839
7840 #if ENABLE_HISTORIC_CHUNKS
7841 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7842 {
7843   int chunk_size = 0;
7844   int x, y;
7845
7846   for (y = 0; y < level->fieldy; y++)
7847     for (x = 0; x < level->fieldx; x++)
7848       if (level->encoding_16bit_field)
7849         chunk_size += putFile16BitBE(file, level->field[x][y]);
7850       else
7851         chunk_size += putFile8Bit(file, level->field[x][y]);
7852
7853   return chunk_size;
7854 }
7855 #endif
7856
7857 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7858 {
7859   int chunk_size = 0;
7860   int x, y;
7861
7862   for (y = 0; y < level->fieldy; y++) 
7863     for (x = 0; x < level->fieldx; x++) 
7864       chunk_size += putFile16BitBE(file, level->field[x][y]);
7865
7866   return chunk_size;
7867 }
7868
7869 #if ENABLE_HISTORIC_CHUNKS
7870 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7871 {
7872   int i, x, y;
7873
7874   putFile8Bit(file, EL_YAMYAM);
7875   putFile8Bit(file, level->num_yamyam_contents);
7876   putFile8Bit(file, 0);
7877   putFile8Bit(file, 0);
7878
7879   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7880     for (y = 0; y < 3; y++)
7881       for (x = 0; x < 3; x++)
7882         if (level->encoding_16bit_field)
7883           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7884         else
7885           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7886 }
7887 #endif
7888
7889 #if ENABLE_HISTORIC_CHUNKS
7890 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7891 {
7892   int i, x, y;
7893   int num_contents, content_xsize, content_ysize;
7894   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7895
7896   if (element == EL_YAMYAM)
7897   {
7898     num_contents = level->num_yamyam_contents;
7899     content_xsize = 3;
7900     content_ysize = 3;
7901
7902     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7903       for (y = 0; y < 3; y++)
7904         for (x = 0; x < 3; x++)
7905           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7906   }
7907   else if (element == EL_BD_AMOEBA)
7908   {
7909     num_contents = 1;
7910     content_xsize = 1;
7911     content_ysize = 1;
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           content_array[i][x][y] = EL_EMPTY;
7917     content_array[0][0][0] = level->amoeba_content;
7918   }
7919   else
7920   {
7921     // chunk header already written -- write empty chunk data
7922     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7923
7924     Warn("cannot save content for element '%d'", element);
7925
7926     return;
7927   }
7928
7929   putFile16BitBE(file, element);
7930   putFile8Bit(file, num_contents);
7931   putFile8Bit(file, content_xsize);
7932   putFile8Bit(file, content_ysize);
7933
7934   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
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         putFile16BitBE(file, content_array[i][x][y]);
7940 }
7941 #endif
7942
7943 #if ENABLE_HISTORIC_CHUNKS
7944 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7945 {
7946   int envelope_nr = element - EL_ENVELOPE_1;
7947   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7948   int chunk_size = 0;
7949   int i;
7950
7951   chunk_size += putFile16BitBE(file, element);
7952   chunk_size += putFile16BitBE(file, envelope_len);
7953   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7954   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7955
7956   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7957   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7958
7959   for (i = 0; i < envelope_len; i++)
7960     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7961
7962   return chunk_size;
7963 }
7964 #endif
7965
7966 #if ENABLE_HISTORIC_CHUNKS
7967 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
7968                            int num_changed_custom_elements)
7969 {
7970   int i, check = 0;
7971
7972   putFile16BitBE(file, num_changed_custom_elements);
7973
7974   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7975   {
7976     int element = EL_CUSTOM_START + i;
7977
7978     struct ElementInfo *ei = &element_info[element];
7979
7980     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
7981     {
7982       if (check < num_changed_custom_elements)
7983       {
7984         putFile16BitBE(file, element);
7985         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
7986       }
7987
7988       check++;
7989     }
7990   }
7991
7992   if (check != num_changed_custom_elements)     // should not happen
7993     Warn("inconsistent number of custom element properties");
7994 }
7995 #endif
7996
7997 #if ENABLE_HISTORIC_CHUNKS
7998 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
7999                            int num_changed_custom_elements)
8000 {
8001   int i, check = 0;
8002
8003   putFile16BitBE(file, num_changed_custom_elements);
8004
8005   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8006   {
8007     int element = EL_CUSTOM_START + i;
8008
8009     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8010     {
8011       if (check < num_changed_custom_elements)
8012       {
8013         putFile16BitBE(file, element);
8014         putFile16BitBE(file, element_info[element].change->target_element);
8015       }
8016
8017       check++;
8018     }
8019   }
8020
8021   if (check != num_changed_custom_elements)     // should not happen
8022     Warn("inconsistent number of custom target elements");
8023 }
8024 #endif
8025
8026 #if ENABLE_HISTORIC_CHUNKS
8027 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8028                            int num_changed_custom_elements)
8029 {
8030   int i, j, x, y, check = 0;
8031
8032   putFile16BitBE(file, num_changed_custom_elements);
8033
8034   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8035   {
8036     int element = EL_CUSTOM_START + i;
8037     struct ElementInfo *ei = &element_info[element];
8038
8039     if (ei->modified_settings)
8040     {
8041       if (check < num_changed_custom_elements)
8042       {
8043         putFile16BitBE(file, element);
8044
8045         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8046           putFile8Bit(file, ei->description[j]);
8047
8048         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8049
8050         // some free bytes for future properties and padding
8051         WriteUnusedBytesToFile(file, 7);
8052
8053         putFile8Bit(file, ei->use_gfx_element);
8054         putFile16BitBE(file, ei->gfx_element_initial);
8055
8056         putFile8Bit(file, ei->collect_score_initial);
8057         putFile8Bit(file, ei->collect_count_initial);
8058
8059         putFile16BitBE(file, ei->push_delay_fixed);
8060         putFile16BitBE(file, ei->push_delay_random);
8061         putFile16BitBE(file, ei->move_delay_fixed);
8062         putFile16BitBE(file, ei->move_delay_random);
8063
8064         putFile16BitBE(file, ei->move_pattern);
8065         putFile8Bit(file, ei->move_direction_initial);
8066         putFile8Bit(file, ei->move_stepsize);
8067
8068         for (y = 0; y < 3; y++)
8069           for (x = 0; x < 3; x++)
8070             putFile16BitBE(file, ei->content.e[x][y]);
8071
8072         putFile32BitBE(file, ei->change->events);
8073
8074         putFile16BitBE(file, ei->change->target_element);
8075
8076         putFile16BitBE(file, ei->change->delay_fixed);
8077         putFile16BitBE(file, ei->change->delay_random);
8078         putFile16BitBE(file, ei->change->delay_frames);
8079
8080         putFile16BitBE(file, ei->change->initial_trigger_element);
8081
8082         putFile8Bit(file, ei->change->explode);
8083         putFile8Bit(file, ei->change->use_target_content);
8084         putFile8Bit(file, ei->change->only_if_complete);
8085         putFile8Bit(file, ei->change->use_random_replace);
8086
8087         putFile8Bit(file, ei->change->random_percentage);
8088         putFile8Bit(file, ei->change->replace_when);
8089
8090         for (y = 0; y < 3; y++)
8091           for (x = 0; x < 3; x++)
8092             putFile16BitBE(file, ei->change->content.e[x][y]);
8093
8094         putFile8Bit(file, ei->slippery_type);
8095
8096         // some free bytes for future properties and padding
8097         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8098       }
8099
8100       check++;
8101     }
8102   }
8103
8104   if (check != num_changed_custom_elements)     // should not happen
8105     Warn("inconsistent number of custom element properties");
8106 }
8107 #endif
8108
8109 #if ENABLE_HISTORIC_CHUNKS
8110 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8111 {
8112   struct ElementInfo *ei = &element_info[element];
8113   int i, j, x, y;
8114
8115   // ---------- custom element base property values (96 bytes) ----------------
8116
8117   putFile16BitBE(file, element);
8118
8119   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8120     putFile8Bit(file, ei->description[i]);
8121
8122   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8123
8124   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8125
8126   putFile8Bit(file, ei->num_change_pages);
8127
8128   putFile16BitBE(file, ei->ce_value_fixed_initial);
8129   putFile16BitBE(file, ei->ce_value_random_initial);
8130   putFile8Bit(file, ei->use_last_ce_value);
8131
8132   putFile8Bit(file, ei->use_gfx_element);
8133   putFile16BitBE(file, ei->gfx_element_initial);
8134
8135   putFile8Bit(file, ei->collect_score_initial);
8136   putFile8Bit(file, ei->collect_count_initial);
8137
8138   putFile8Bit(file, ei->drop_delay_fixed);
8139   putFile8Bit(file, ei->push_delay_fixed);
8140   putFile8Bit(file, ei->drop_delay_random);
8141   putFile8Bit(file, ei->push_delay_random);
8142   putFile16BitBE(file, ei->move_delay_fixed);
8143   putFile16BitBE(file, ei->move_delay_random);
8144
8145   // bits 0 - 15 of "move_pattern" ...
8146   putFile16BitBE(file, ei->move_pattern & 0xffff);
8147   putFile8Bit(file, ei->move_direction_initial);
8148   putFile8Bit(file, ei->move_stepsize);
8149
8150   putFile8Bit(file, ei->slippery_type);
8151
8152   for (y = 0; y < 3; y++)
8153     for (x = 0; x < 3; x++)
8154       putFile16BitBE(file, ei->content.e[x][y]);
8155
8156   putFile16BitBE(file, ei->move_enter_element);
8157   putFile16BitBE(file, ei->move_leave_element);
8158   putFile8Bit(file, ei->move_leave_type);
8159
8160   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8161   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8162
8163   putFile8Bit(file, ei->access_direction);
8164
8165   putFile8Bit(file, ei->explosion_delay);
8166   putFile8Bit(file, ei->ignition_delay);
8167   putFile8Bit(file, ei->explosion_type);
8168
8169   // some free bytes for future custom property values and padding
8170   WriteUnusedBytesToFile(file, 1);
8171
8172   // ---------- change page property values (48 bytes) ------------------------
8173
8174   for (i = 0; i < ei->num_change_pages; i++)
8175   {
8176     struct ElementChangeInfo *change = &ei->change_page[i];
8177     unsigned int event_bits;
8178
8179     // bits 0 - 31 of "has_event[]" ...
8180     event_bits = 0;
8181     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8182       if (change->has_event[j])
8183         event_bits |= (1u << j);
8184     putFile32BitBE(file, event_bits);
8185
8186     putFile16BitBE(file, change->target_element);
8187
8188     putFile16BitBE(file, change->delay_fixed);
8189     putFile16BitBE(file, change->delay_random);
8190     putFile16BitBE(file, change->delay_frames);
8191
8192     putFile16BitBE(file, change->initial_trigger_element);
8193
8194     putFile8Bit(file, change->explode);
8195     putFile8Bit(file, change->use_target_content);
8196     putFile8Bit(file, change->only_if_complete);
8197     putFile8Bit(file, change->use_random_replace);
8198
8199     putFile8Bit(file, change->random_percentage);
8200     putFile8Bit(file, change->replace_when);
8201
8202     for (y = 0; y < 3; y++)
8203       for (x = 0; x < 3; x++)
8204         putFile16BitBE(file, change->target_content.e[x][y]);
8205
8206     putFile8Bit(file, change->can_change);
8207
8208     putFile8Bit(file, change->trigger_side);
8209
8210     putFile8Bit(file, change->trigger_player);
8211     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8212                        log_2(change->trigger_page)));
8213
8214     putFile8Bit(file, change->has_action);
8215     putFile8Bit(file, change->action_type);
8216     putFile8Bit(file, change->action_mode);
8217     putFile16BitBE(file, change->action_arg);
8218
8219     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8220     event_bits = 0;
8221     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8222       if (change->has_event[j])
8223         event_bits |= (1u << (j - 32));
8224     putFile8Bit(file, event_bits);
8225   }
8226 }
8227 #endif
8228
8229 #if ENABLE_HISTORIC_CHUNKS
8230 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8231 {
8232   struct ElementInfo *ei = &element_info[element];
8233   struct ElementGroupInfo *group = ei->group;
8234   int i;
8235
8236   putFile16BitBE(file, element);
8237
8238   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8239     putFile8Bit(file, ei->description[i]);
8240
8241   putFile8Bit(file, group->num_elements);
8242
8243   putFile8Bit(file, ei->use_gfx_element);
8244   putFile16BitBE(file, ei->gfx_element_initial);
8245
8246   putFile8Bit(file, group->choice_mode);
8247
8248   // some free bytes for future values and padding
8249   WriteUnusedBytesToFile(file, 3);
8250
8251   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8252     putFile16BitBE(file, group->element[i]);
8253 }
8254 #endif
8255
8256 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8257                                 boolean write_element)
8258 {
8259   int save_type = entry->save_type;
8260   int data_type = entry->data_type;
8261   int conf_type = entry->conf_type;
8262   int byte_mask = conf_type & CONF_MASK_BYTES;
8263   int element = entry->element;
8264   int default_value = entry->default_value;
8265   int num_bytes = 0;
8266   boolean modified = FALSE;
8267
8268   if (byte_mask != CONF_MASK_MULTI_BYTES)
8269   {
8270     void *value_ptr = entry->value;
8271     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8272                  *(int *)value_ptr);
8273
8274     // check if any settings have been modified before saving them
8275     if (value != default_value)
8276       modified = TRUE;
8277
8278     // do not save if explicitly told or if unmodified default settings
8279     if ((save_type == SAVE_CONF_NEVER) ||
8280         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8281       return 0;
8282
8283     if (write_element)
8284       num_bytes += putFile16BitBE(file, element);
8285
8286     num_bytes += putFile8Bit(file, conf_type);
8287     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8288                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8289                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8290                   0);
8291   }
8292   else if (data_type == TYPE_STRING)
8293   {
8294     char *default_string = entry->default_string;
8295     char *string = (char *)(entry->value);
8296     int string_length = strlen(string);
8297     int i;
8298
8299     // check if any settings have been modified before saving them
8300     if (!strEqual(string, default_string))
8301       modified = TRUE;
8302
8303     // do not save if explicitly told or if unmodified default settings
8304     if ((save_type == SAVE_CONF_NEVER) ||
8305         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8306       return 0;
8307
8308     if (write_element)
8309       num_bytes += putFile16BitBE(file, element);
8310
8311     num_bytes += putFile8Bit(file, conf_type);
8312     num_bytes += putFile16BitBE(file, string_length);
8313
8314     for (i = 0; i < string_length; i++)
8315       num_bytes += putFile8Bit(file, string[i]);
8316   }
8317   else if (data_type == TYPE_ELEMENT_LIST)
8318   {
8319     int *element_array = (int *)(entry->value);
8320     int num_elements = *(int *)(entry->num_entities);
8321     int i;
8322
8323     // check if any settings have been modified before saving them
8324     for (i = 0; i < num_elements; i++)
8325       if (element_array[i] != default_value)
8326         modified = TRUE;
8327
8328     // do not save if explicitly told or if unmodified default settings
8329     if ((save_type == SAVE_CONF_NEVER) ||
8330         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8331       return 0;
8332
8333     if (write_element)
8334       num_bytes += putFile16BitBE(file, element);
8335
8336     num_bytes += putFile8Bit(file, conf_type);
8337     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8338
8339     for (i = 0; i < num_elements; i++)
8340       num_bytes += putFile16BitBE(file, element_array[i]);
8341   }
8342   else if (data_type == TYPE_CONTENT_LIST)
8343   {
8344     struct Content *content = (struct Content *)(entry->value);
8345     int num_contents = *(int *)(entry->num_entities);
8346     int i, x, y;
8347
8348     // check if any settings have been modified before saving them
8349     for (i = 0; i < num_contents; i++)
8350       for (y = 0; y < 3; y++)
8351         for (x = 0; x < 3; x++)
8352           if (content[i].e[x][y] != default_value)
8353             modified = TRUE;
8354
8355     // do not save if explicitly told or if unmodified default settings
8356     if ((save_type == SAVE_CONF_NEVER) ||
8357         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8358       return 0;
8359
8360     if (write_element)
8361       num_bytes += putFile16BitBE(file, element);
8362
8363     num_bytes += putFile8Bit(file, conf_type);
8364     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8365
8366     for (i = 0; i < num_contents; i++)
8367       for (y = 0; y < 3; y++)
8368         for (x = 0; x < 3; x++)
8369           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8370   }
8371
8372   return num_bytes;
8373 }
8374
8375 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8376 {
8377   int chunk_size = 0;
8378   int i;
8379
8380   li = *level;          // copy level data into temporary buffer
8381
8382   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8383     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8384
8385   return chunk_size;
8386 }
8387
8388 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8389 {
8390   int chunk_size = 0;
8391   int i;
8392
8393   li = *level;          // copy level data into temporary buffer
8394
8395   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8396     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8397
8398   return chunk_size;
8399 }
8400
8401 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8402 {
8403   int envelope_nr = element - EL_ENVELOPE_1;
8404   int chunk_size = 0;
8405   int i;
8406
8407   chunk_size += putFile16BitBE(file, element);
8408
8409   // copy envelope data into temporary buffer
8410   xx_envelope = level->envelope[envelope_nr];
8411
8412   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8413     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8414
8415   return chunk_size;
8416 }
8417
8418 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8419 {
8420   struct ElementInfo *ei = &element_info[element];
8421   int chunk_size = 0;
8422   int i, j;
8423
8424   chunk_size += putFile16BitBE(file, element);
8425
8426   xx_ei = *ei;          // copy element data into temporary buffer
8427
8428   // set default description string for this specific element
8429   strcpy(xx_default_description, getDefaultElementDescription(ei));
8430
8431   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8432     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8433
8434   for (i = 0; i < ei->num_change_pages; i++)
8435   {
8436     struct ElementChangeInfo *change = &ei->change_page[i];
8437
8438     xx_current_change_page = i;
8439
8440     xx_change = *change;        // copy change data into temporary buffer
8441
8442     resetEventBits();
8443     setEventBitsFromEventFlags(change);
8444
8445     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8446       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8447                                          FALSE);
8448   }
8449
8450   return chunk_size;
8451 }
8452
8453 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8454 {
8455   struct ElementInfo *ei = &element_info[element];
8456   struct ElementGroupInfo *group = ei->group;
8457   int chunk_size = 0;
8458   int i;
8459
8460   chunk_size += putFile16BitBE(file, element);
8461
8462   xx_ei = *ei;          // copy element data into temporary buffer
8463   xx_group = *group;    // copy group data into temporary buffer
8464
8465   // set default description string for this specific element
8466   strcpy(xx_default_description, getDefaultElementDescription(ei));
8467
8468   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8469     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8470
8471   return chunk_size;
8472 }
8473
8474 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8475 {
8476   struct ElementInfo *ei = &element_info[element];
8477   int chunk_size = 0;
8478   int i;
8479
8480   chunk_size += putFile16BitBE(file, element);
8481
8482   xx_ei = *ei;          // copy element data into temporary buffer
8483
8484   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8485     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8486
8487   return chunk_size;
8488 }
8489
8490 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8491                                   boolean save_as_template)
8492 {
8493   int chunk_size;
8494   int i;
8495   FILE *file;
8496
8497   if (!(file = fopen(filename, MODE_WRITE)))
8498   {
8499     Warn("cannot save level file '%s'", filename);
8500
8501     return;
8502   }
8503
8504   level->file_version = FILE_VERSION_ACTUAL;
8505   level->game_version = GAME_VERSION_ACTUAL;
8506
8507   level->creation_date = getCurrentDate();
8508
8509   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8510   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8511
8512   chunk_size = SaveLevel_VERS(NULL, level);
8513   putFileChunkBE(file, "VERS", chunk_size);
8514   SaveLevel_VERS(file, level);
8515
8516   chunk_size = SaveLevel_DATE(NULL, level);
8517   putFileChunkBE(file, "DATE", chunk_size);
8518   SaveLevel_DATE(file, level);
8519
8520   chunk_size = SaveLevel_NAME(NULL, level);
8521   putFileChunkBE(file, "NAME", chunk_size);
8522   SaveLevel_NAME(file, level);
8523
8524   chunk_size = SaveLevel_AUTH(NULL, level);
8525   putFileChunkBE(file, "AUTH", chunk_size);
8526   SaveLevel_AUTH(file, level);
8527
8528   chunk_size = SaveLevel_INFO(NULL, level);
8529   putFileChunkBE(file, "INFO", chunk_size);
8530   SaveLevel_INFO(file, level);
8531
8532   chunk_size = SaveLevel_BODY(NULL, level);
8533   putFileChunkBE(file, "BODY", chunk_size);
8534   SaveLevel_BODY(file, level);
8535
8536   chunk_size = SaveLevel_ELEM(NULL, level);
8537   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8538   {
8539     putFileChunkBE(file, "ELEM", chunk_size);
8540     SaveLevel_ELEM(file, level);
8541   }
8542
8543   for (i = 0; i < NUM_ENVELOPES; i++)
8544   {
8545     int element = EL_ENVELOPE_1 + i;
8546
8547     chunk_size = SaveLevel_NOTE(NULL, level, element);
8548     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8549     {
8550       putFileChunkBE(file, "NOTE", chunk_size);
8551       SaveLevel_NOTE(file, level, element);
8552     }
8553   }
8554
8555   // if not using template level, check for non-default custom/group elements
8556   if (!level->use_custom_template || save_as_template)
8557   {
8558     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8559     {
8560       int element = EL_CUSTOM_START + i;
8561
8562       chunk_size = SaveLevel_CUSX(NULL, level, element);
8563       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8564       {
8565         putFileChunkBE(file, "CUSX", chunk_size);
8566         SaveLevel_CUSX(file, level, element);
8567       }
8568     }
8569
8570     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8571     {
8572       int element = EL_GROUP_START + i;
8573
8574       chunk_size = SaveLevel_GRPX(NULL, level, element);
8575       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8576       {
8577         putFileChunkBE(file, "GRPX", chunk_size);
8578         SaveLevel_GRPX(file, level, element);
8579       }
8580     }
8581
8582     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8583     {
8584       int element = GET_EMPTY_ELEMENT(i);
8585
8586       chunk_size = SaveLevel_EMPX(NULL, level, element);
8587       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8588       {
8589         putFileChunkBE(file, "EMPX", chunk_size);
8590         SaveLevel_EMPX(file, level, element);
8591       }
8592     }
8593   }
8594
8595   fclose(file);
8596
8597   SetFilePermissions(filename, PERMS_PRIVATE);
8598 }
8599
8600 void SaveLevel(int nr)
8601 {
8602   char *filename = getDefaultLevelFilename(nr);
8603
8604   SaveLevelFromFilename(&level, filename, FALSE);
8605 }
8606
8607 void SaveLevelTemplate(void)
8608 {
8609   char *filename = getLocalLevelTemplateFilename();
8610
8611   SaveLevelFromFilename(&level, filename, TRUE);
8612 }
8613
8614 boolean SaveLevelChecked(int nr)
8615 {
8616   char *filename = getDefaultLevelFilename(nr);
8617   boolean new_level = !fileExists(filename);
8618   boolean level_saved = FALSE;
8619
8620   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8621   {
8622     SaveLevel(nr);
8623
8624     if (new_level)
8625       Request("Level saved!", REQ_CONFIRM);
8626
8627     level_saved = TRUE;
8628   }
8629
8630   return level_saved;
8631 }
8632
8633 void DumpLevel(struct LevelInfo *level)
8634 {
8635   if (level->no_level_file || level->no_valid_file)
8636   {
8637     Warn("cannot dump -- no valid level file found");
8638
8639     return;
8640   }
8641
8642   PrintLine("-", 79);
8643   Print("Level xxx (file version %08d, game version %08d)\n",
8644         level->file_version, level->game_version);
8645   PrintLine("-", 79);
8646
8647   Print("Level author: '%s'\n", level->author);
8648   Print("Level title:  '%s'\n", level->name);
8649   Print("\n");
8650   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8651   Print("\n");
8652   Print("Level time:  %d seconds\n", level->time);
8653   Print("Gems needed: %d\n", level->gems_needed);
8654   Print("\n");
8655   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8656   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8657   Print("Time for light:      %d seconds\n", level->time_light);
8658   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8659   Print("\n");
8660   Print("Amoeba speed: %d\n", level->amoeba_speed);
8661   Print("\n");
8662
8663   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8664   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8665   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8666   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8667   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8668   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8669
8670   if (options.debug)
8671   {
8672     int i, j;
8673
8674     for (i = 0; i < NUM_ENVELOPES; i++)
8675     {
8676       char *text = level->envelope[i].text;
8677       int text_len = strlen(text);
8678       boolean has_text = FALSE;
8679
8680       for (j = 0; j < text_len; j++)
8681         if (text[j] != ' ' && text[j] != '\n')
8682           has_text = TRUE;
8683
8684       if (has_text)
8685       {
8686         Print("\n");
8687         Print("Envelope %d:\n'%s'\n", i + 1, text);
8688       }
8689     }
8690   }
8691
8692   PrintLine("-", 79);
8693 }
8694
8695 void DumpLevels(void)
8696 {
8697   static LevelDirTree *dumplevel_leveldir = NULL;
8698
8699   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8700                                                  global.dumplevel_leveldir);
8701
8702   if (dumplevel_leveldir == NULL)
8703     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8704
8705   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8706       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8707     Fail("no such level number: %d", global.dumplevel_level_nr);
8708
8709   leveldir_current = dumplevel_leveldir;
8710
8711   LoadLevel(global.dumplevel_level_nr);
8712   DumpLevel(&level);
8713
8714   CloseAllAndExit(0);
8715 }
8716
8717
8718 // ============================================================================
8719 // tape file functions
8720 // ============================================================================
8721
8722 static void setTapeInfoToDefaults(void)
8723 {
8724   int i;
8725
8726   // always start with reliable default values (empty tape)
8727   TapeErase();
8728
8729   // default values (also for pre-1.2 tapes) with only the first player
8730   tape.player_participates[0] = TRUE;
8731   for (i = 1; i < MAX_PLAYERS; i++)
8732     tape.player_participates[i] = FALSE;
8733
8734   // at least one (default: the first) player participates in every tape
8735   tape.num_participating_players = 1;
8736
8737   tape.property_bits = TAPE_PROPERTY_NONE;
8738
8739   tape.level_nr = level_nr;
8740   tape.counter = 0;
8741   tape.changed = FALSE;
8742   tape.solved = FALSE;
8743
8744   tape.recording = FALSE;
8745   tape.playing = FALSE;
8746   tape.pausing = FALSE;
8747
8748   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8749   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8750
8751   tape.no_info_chunk = TRUE;
8752   tape.no_valid_file = FALSE;
8753 }
8754
8755 static int getTapePosSize(struct TapeInfo *tape)
8756 {
8757   int tape_pos_size = 0;
8758
8759   if (tape->use_key_actions)
8760     tape_pos_size += tape->num_participating_players;
8761
8762   if (tape->use_mouse_actions)
8763     tape_pos_size += 3;         // x and y position and mouse button mask
8764
8765   tape_pos_size += 1;           // tape action delay value
8766
8767   return tape_pos_size;
8768 }
8769
8770 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8771 {
8772   tape->use_key_actions = FALSE;
8773   tape->use_mouse_actions = FALSE;
8774
8775   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8776     tape->use_key_actions = TRUE;
8777
8778   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8779     tape->use_mouse_actions = TRUE;
8780 }
8781
8782 static int getTapeActionValue(struct TapeInfo *tape)
8783 {
8784   return (tape->use_key_actions &&
8785           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8786           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8787           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8788           TAPE_ACTIONS_DEFAULT);
8789 }
8790
8791 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8792 {
8793   tape->file_version = getFileVersion(file);
8794   tape->game_version = getFileVersion(file);
8795
8796   return chunk_size;
8797 }
8798
8799 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8800 {
8801   int i;
8802
8803   tape->random_seed = getFile32BitBE(file);
8804   tape->date        = getFile32BitBE(file);
8805   tape->length      = getFile32BitBE(file);
8806
8807   // read header fields that are new since version 1.2
8808   if (tape->file_version >= FILE_VERSION_1_2)
8809   {
8810     byte store_participating_players = getFile8Bit(file);
8811     int engine_version;
8812
8813     // since version 1.2, tapes store which players participate in the tape
8814     tape->num_participating_players = 0;
8815     for (i = 0; i < MAX_PLAYERS; i++)
8816     {
8817       tape->player_participates[i] = FALSE;
8818
8819       if (store_participating_players & (1 << i))
8820       {
8821         tape->player_participates[i] = TRUE;
8822         tape->num_participating_players++;
8823       }
8824     }
8825
8826     setTapeActionFlags(tape, getFile8Bit(file));
8827
8828     tape->property_bits = getFile8Bit(file);
8829     tape->solved = getFile8Bit(file);
8830
8831     engine_version = getFileVersion(file);
8832     if (engine_version > 0)
8833       tape->engine_version = engine_version;
8834     else
8835       tape->engine_version = tape->game_version;
8836   }
8837
8838   return chunk_size;
8839 }
8840
8841 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8842 {
8843   tape->scr_fieldx = getFile8Bit(file);
8844   tape->scr_fieldy = getFile8Bit(file);
8845
8846   return chunk_size;
8847 }
8848
8849 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8850 {
8851   char *level_identifier = NULL;
8852   int level_identifier_size;
8853   int i;
8854
8855   tape->no_info_chunk = FALSE;
8856
8857   level_identifier_size = getFile16BitBE(file);
8858
8859   level_identifier = checked_malloc(level_identifier_size);
8860
8861   for (i = 0; i < level_identifier_size; i++)
8862     level_identifier[i] = getFile8Bit(file);
8863
8864   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8865   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8866
8867   checked_free(level_identifier);
8868
8869   tape->level_nr = getFile16BitBE(file);
8870
8871   chunk_size = 2 + level_identifier_size + 2;
8872
8873   return chunk_size;
8874 }
8875
8876 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8877 {
8878   int i, j;
8879   int tape_pos_size = getTapePosSize(tape);
8880   int chunk_size_expected = tape_pos_size * tape->length;
8881
8882   if (chunk_size_expected != chunk_size)
8883   {
8884     ReadUnusedBytesFromFile(file, chunk_size);
8885     return chunk_size_expected;
8886   }
8887
8888   for (i = 0; i < tape->length; i++)
8889   {
8890     if (i >= MAX_TAPE_LEN)
8891     {
8892       Warn("tape truncated -- size exceeds maximum tape size %d",
8893             MAX_TAPE_LEN);
8894
8895       // tape too large; read and ignore remaining tape data from this chunk
8896       for (;i < tape->length; i++)
8897         ReadUnusedBytesFromFile(file, tape_pos_size);
8898
8899       break;
8900     }
8901
8902     if (tape->use_key_actions)
8903     {
8904       for (j = 0; j < MAX_PLAYERS; j++)
8905       {
8906         tape->pos[i].action[j] = MV_NONE;
8907
8908         if (tape->player_participates[j])
8909           tape->pos[i].action[j] = getFile8Bit(file);
8910       }
8911     }
8912
8913     if (tape->use_mouse_actions)
8914     {
8915       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8916       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8917       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8918     }
8919
8920     tape->pos[i].delay = getFile8Bit(file);
8921
8922     if (tape->file_version == FILE_VERSION_1_0)
8923     {
8924       // eliminate possible diagonal moves in old tapes
8925       // this is only for backward compatibility
8926
8927       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8928       byte action = tape->pos[i].action[0];
8929       int k, num_moves = 0;
8930
8931       for (k = 0; k < 4; k++)
8932       {
8933         if (action & joy_dir[k])
8934         {
8935           tape->pos[i + num_moves].action[0] = joy_dir[k];
8936           if (num_moves > 0)
8937             tape->pos[i + num_moves].delay = 0;
8938           num_moves++;
8939         }
8940       }
8941
8942       if (num_moves > 1)
8943       {
8944         num_moves--;
8945         i += num_moves;
8946         tape->length += num_moves;
8947       }
8948     }
8949     else if (tape->file_version < FILE_VERSION_2_0)
8950     {
8951       // convert pre-2.0 tapes to new tape format
8952
8953       if (tape->pos[i].delay > 1)
8954       {
8955         // action part
8956         tape->pos[i + 1] = tape->pos[i];
8957         tape->pos[i + 1].delay = 1;
8958
8959         // delay part
8960         for (j = 0; j < MAX_PLAYERS; j++)
8961           tape->pos[i].action[j] = MV_NONE;
8962         tape->pos[i].delay--;
8963
8964         i++;
8965         tape->length++;
8966       }
8967     }
8968
8969     if (checkEndOfFile(file))
8970       break;
8971   }
8972
8973   if (i != tape->length)
8974     chunk_size = tape_pos_size * i;
8975
8976   return chunk_size;
8977 }
8978
8979 static void LoadTape_SokobanSolution(char *filename)
8980 {
8981   File *file;
8982   int move_delay = TILESIZE / level.initial_player_stepsize[0];
8983
8984   if (!(file = openFile(filename, MODE_READ)))
8985   {
8986     tape.no_valid_file = TRUE;
8987
8988     return;
8989   }
8990
8991   while (!checkEndOfFile(file))
8992   {
8993     unsigned char c = getByteFromFile(file);
8994
8995     if (checkEndOfFile(file))
8996       break;
8997
8998     switch (c)
8999     {
9000       case 'u':
9001       case 'U':
9002         tape.pos[tape.length].action[0] = MV_UP;
9003         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9004         tape.length++;
9005         break;
9006
9007       case 'd':
9008       case 'D':
9009         tape.pos[tape.length].action[0] = MV_DOWN;
9010         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9011         tape.length++;
9012         break;
9013
9014       case 'l':
9015       case 'L':
9016         tape.pos[tape.length].action[0] = MV_LEFT;
9017         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9018         tape.length++;
9019         break;
9020
9021       case 'r':
9022       case 'R':
9023         tape.pos[tape.length].action[0] = MV_RIGHT;
9024         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9025         tape.length++;
9026         break;
9027
9028       case '\n':
9029       case '\r':
9030       case '\t':
9031       case ' ':
9032         // ignore white-space characters
9033         break;
9034
9035       default:
9036         tape.no_valid_file = TRUE;
9037
9038         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9039
9040         break;
9041     }
9042   }
9043
9044   closeFile(file);
9045
9046   if (tape.no_valid_file)
9047     return;
9048
9049   tape.length_frames  = GetTapeLengthFrames();
9050   tape.length_seconds = GetTapeLengthSeconds();
9051 }
9052
9053 void LoadTapeFromFilename(char *filename)
9054 {
9055   char cookie[MAX_LINE_LEN];
9056   char chunk_name[CHUNK_ID_LEN + 1];
9057   File *file;
9058   int chunk_size;
9059
9060   // always start with reliable default values
9061   setTapeInfoToDefaults();
9062
9063   if (strSuffix(filename, ".sln"))
9064   {
9065     LoadTape_SokobanSolution(filename);
9066
9067     return;
9068   }
9069
9070   if (!(file = openFile(filename, MODE_READ)))
9071   {
9072     tape.no_valid_file = TRUE;
9073
9074     return;
9075   }
9076
9077   getFileChunkBE(file, chunk_name, NULL);
9078   if (strEqual(chunk_name, "RND1"))
9079   {
9080     getFile32BitBE(file);               // not used
9081
9082     getFileChunkBE(file, chunk_name, NULL);
9083     if (!strEqual(chunk_name, "TAPE"))
9084     {
9085       tape.no_valid_file = TRUE;
9086
9087       Warn("unknown format of tape file '%s'", filename);
9088
9089       closeFile(file);
9090
9091       return;
9092     }
9093   }
9094   else  // check for pre-2.0 file format with cookie string
9095   {
9096     strcpy(cookie, chunk_name);
9097     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9098       cookie[4] = '\0';
9099     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9100       cookie[strlen(cookie) - 1] = '\0';
9101
9102     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9103     {
9104       tape.no_valid_file = TRUE;
9105
9106       Warn("unknown format of tape file '%s'", filename);
9107
9108       closeFile(file);
9109
9110       return;
9111     }
9112
9113     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9114     {
9115       tape.no_valid_file = TRUE;
9116
9117       Warn("unsupported version of tape file '%s'", filename);
9118
9119       closeFile(file);
9120
9121       return;
9122     }
9123
9124     // pre-2.0 tape files have no game version, so use file version here
9125     tape.game_version = tape.file_version;
9126   }
9127
9128   if (tape.file_version < FILE_VERSION_1_2)
9129   {
9130     // tape files from versions before 1.2.0 without chunk structure
9131     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9132     LoadTape_BODY(file, 2 * tape.length,      &tape);
9133   }
9134   else
9135   {
9136     static struct
9137     {
9138       char *name;
9139       int size;
9140       int (*loader)(File *, int, struct TapeInfo *);
9141     }
9142     chunk_info[] =
9143     {
9144       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9145       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9146       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9147       { "INFO", -1,                     LoadTape_INFO },
9148       { "BODY", -1,                     LoadTape_BODY },
9149       {  NULL,  0,                      NULL }
9150     };
9151
9152     while (getFileChunkBE(file, chunk_name, &chunk_size))
9153     {
9154       int i = 0;
9155
9156       while (chunk_info[i].name != NULL &&
9157              !strEqual(chunk_name, chunk_info[i].name))
9158         i++;
9159
9160       if (chunk_info[i].name == NULL)
9161       {
9162         Warn("unknown chunk '%s' in tape file '%s'",
9163               chunk_name, filename);
9164
9165         ReadUnusedBytesFromFile(file, chunk_size);
9166       }
9167       else if (chunk_info[i].size != -1 &&
9168                chunk_info[i].size != chunk_size)
9169       {
9170         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9171               chunk_size, chunk_name, filename);
9172
9173         ReadUnusedBytesFromFile(file, chunk_size);
9174       }
9175       else
9176       {
9177         // call function to load this tape chunk
9178         int chunk_size_expected =
9179           (chunk_info[i].loader)(file, chunk_size, &tape);
9180
9181         // the size of some chunks cannot be checked before reading other
9182         // chunks first (like "HEAD" and "BODY") that contain some header
9183         // information, so check them here
9184         if (chunk_size_expected != chunk_size)
9185         {
9186           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9187                 chunk_size, chunk_name, filename);
9188         }
9189       }
9190     }
9191   }
9192
9193   closeFile(file);
9194
9195   tape.length_frames  = GetTapeLengthFrames();
9196   tape.length_seconds = GetTapeLengthSeconds();
9197
9198 #if 0
9199   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9200         tape.file_version);
9201   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9202         tape.game_version);
9203   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9204         tape.engine_version);
9205 #endif
9206 }
9207
9208 void LoadTape(int nr)
9209 {
9210   char *filename = getTapeFilename(nr);
9211
9212   LoadTapeFromFilename(filename);
9213 }
9214
9215 void LoadSolutionTape(int nr)
9216 {
9217   char *filename = getSolutionTapeFilename(nr);
9218
9219   LoadTapeFromFilename(filename);
9220
9221   if (TAPE_IS_EMPTY(tape))
9222   {
9223     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9224         level.native_bd_level->replay != NULL)
9225       CopyNativeTape_BD_to_RND(&level);
9226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9227         level.native_sp_level->demo.is_available)
9228       CopyNativeTape_SP_to_RND(&level);
9229   }
9230 }
9231
9232 void LoadScoreTape(char *score_tape_basename, int nr)
9233 {
9234   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9235
9236   LoadTapeFromFilename(filename);
9237 }
9238
9239 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9240 {
9241   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9242
9243   LoadTapeFromFilename(filename);
9244 }
9245
9246 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9247 {
9248   // chunk required for team mode tapes with non-default screen size
9249   return (tape->num_participating_players > 1 &&
9250           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9251            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9252 }
9253
9254 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9255 {
9256   putFileVersion(file, tape->file_version);
9257   putFileVersion(file, tape->game_version);
9258 }
9259
9260 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9261 {
9262   int i;
9263   byte store_participating_players = 0;
9264
9265   // set bits for participating players for compact storage
9266   for (i = 0; i < MAX_PLAYERS; i++)
9267     if (tape->player_participates[i])
9268       store_participating_players |= (1 << i);
9269
9270   putFile32BitBE(file, tape->random_seed);
9271   putFile32BitBE(file, tape->date);
9272   putFile32BitBE(file, tape->length);
9273
9274   putFile8Bit(file, store_participating_players);
9275
9276   putFile8Bit(file, getTapeActionValue(tape));
9277
9278   putFile8Bit(file, tape->property_bits);
9279   putFile8Bit(file, tape->solved);
9280
9281   putFileVersion(file, tape->engine_version);
9282 }
9283
9284 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9285 {
9286   putFile8Bit(file, tape->scr_fieldx);
9287   putFile8Bit(file, tape->scr_fieldy);
9288 }
9289
9290 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9291 {
9292   int level_identifier_size = strlen(tape->level_identifier) + 1;
9293   int i;
9294
9295   putFile16BitBE(file, level_identifier_size);
9296
9297   for (i = 0; i < level_identifier_size; i++)
9298     putFile8Bit(file, tape->level_identifier[i]);
9299
9300   putFile16BitBE(file, tape->level_nr);
9301 }
9302
9303 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9304 {
9305   int i, j;
9306
9307   for (i = 0; i < tape->length; i++)
9308   {
9309     if (tape->use_key_actions)
9310     {
9311       for (j = 0; j < MAX_PLAYERS; j++)
9312         if (tape->player_participates[j])
9313           putFile8Bit(file, tape->pos[i].action[j]);
9314     }
9315
9316     if (tape->use_mouse_actions)
9317     {
9318       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9319       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9320       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9321     }
9322
9323     putFile8Bit(file, tape->pos[i].delay);
9324   }
9325 }
9326
9327 void SaveTapeToFilename(char *filename)
9328 {
9329   FILE *file;
9330   int tape_pos_size;
9331   int info_chunk_size;
9332   int body_chunk_size;
9333
9334   if (!(file = fopen(filename, MODE_WRITE)))
9335   {
9336     Warn("cannot save level recording file '%s'", filename);
9337
9338     return;
9339   }
9340
9341   tape_pos_size = getTapePosSize(&tape);
9342
9343   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9344   body_chunk_size = tape_pos_size * tape.length;
9345
9346   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9347   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9348
9349   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9350   SaveTape_VERS(file, &tape);
9351
9352   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9353   SaveTape_HEAD(file, &tape);
9354
9355   if (checkSaveTape_SCRN(&tape))
9356   {
9357     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9358     SaveTape_SCRN(file, &tape);
9359   }
9360
9361   putFileChunkBE(file, "INFO", info_chunk_size);
9362   SaveTape_INFO(file, &tape);
9363
9364   putFileChunkBE(file, "BODY", body_chunk_size);
9365   SaveTape_BODY(file, &tape);
9366
9367   fclose(file);
9368
9369   SetFilePermissions(filename, PERMS_PRIVATE);
9370 }
9371
9372 static void SaveTapeExt(char *filename)
9373 {
9374   int i;
9375
9376   tape.file_version = FILE_VERSION_ACTUAL;
9377   tape.game_version = GAME_VERSION_ACTUAL;
9378
9379   tape.num_participating_players = 0;
9380
9381   // count number of participating players
9382   for (i = 0; i < MAX_PLAYERS; i++)
9383     if (tape.player_participates[i])
9384       tape.num_participating_players++;
9385
9386   SaveTapeToFilename(filename);
9387
9388   tape.changed = FALSE;
9389 }
9390
9391 void SaveTape(int nr)
9392 {
9393   char *filename = getTapeFilename(nr);
9394
9395   InitTapeDirectory(leveldir_current->subdir);
9396
9397   SaveTapeExt(filename);
9398 }
9399
9400 void SaveScoreTape(int nr)
9401 {
9402   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9403
9404   // used instead of "leveldir_current->subdir" (for network games)
9405   InitScoreTapeDirectory(levelset.identifier, nr);
9406
9407   SaveTapeExt(filename);
9408 }
9409
9410 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9411                                   unsigned int req_state_added)
9412 {
9413   char *filename = getTapeFilename(nr);
9414   boolean new_tape = !fileExists(filename);
9415   boolean tape_saved = FALSE;
9416
9417   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9418   {
9419     SaveTape(nr);
9420
9421     if (new_tape)
9422       Request(msg_saved, REQ_CONFIRM | req_state_added);
9423
9424     tape_saved = TRUE;
9425   }
9426
9427   return tape_saved;
9428 }
9429
9430 boolean SaveTapeChecked(int nr)
9431 {
9432   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9433 }
9434
9435 boolean SaveTapeChecked_LevelSolved(int nr)
9436 {
9437   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9438                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9439 }
9440
9441 void DumpTape(struct TapeInfo *tape)
9442 {
9443   int tape_frame_counter;
9444   int i, j;
9445
9446   if (tape->no_valid_file)
9447   {
9448     Warn("cannot dump -- no valid tape file found");
9449
9450     return;
9451   }
9452
9453   PrintLine("-", 79);
9454
9455   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9456         tape->level_nr, tape->file_version, tape->game_version);
9457   Print("                  (effective engine version %08d)\n",
9458         tape->engine_version);
9459   Print("Level series identifier: '%s'\n", tape->level_identifier);
9460
9461   Print("Solution tape: %s\n",
9462         tape->solved ? "yes" :
9463         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9464
9465   Print("Special tape properties: ");
9466   if (tape->property_bits == TAPE_PROPERTY_NONE)
9467     Print("[none]");
9468   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9469     Print("[em_random_bug]");
9470   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9471     Print("[game_speed]");
9472   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9473     Print("[pause]");
9474   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9475     Print("[single_step]");
9476   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9477     Print("[snapshot]");
9478   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9479     Print("[replayed]");
9480   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9481     Print("[tas_keys]");
9482   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9483     Print("[small_graphics]");
9484   Print("\n");
9485
9486   int year2 = tape->date / 10000;
9487   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9488   int month_index_raw = (tape->date / 100) % 100;
9489   int month_index = month_index_raw % 12;       // prevent invalid index
9490   int month = month_index + 1;
9491   int day = tape->date % 100;
9492
9493   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9494
9495   PrintLine("-", 79);
9496
9497   tape_frame_counter = 0;
9498
9499   for (i = 0; i < tape->length; i++)
9500   {
9501     if (i >= MAX_TAPE_LEN)
9502       break;
9503
9504     Print("%04d: ", i);
9505
9506     for (j = 0; j < MAX_PLAYERS; j++)
9507     {
9508       if (tape->player_participates[j])
9509       {
9510         int action = tape->pos[i].action[j];
9511
9512         Print("%d:%02x ", j, action);
9513         Print("[%c%c%c%c|%c%c] - ",
9514               (action & JOY_LEFT ? '<' : ' '),
9515               (action & JOY_RIGHT ? '>' : ' '),
9516               (action & JOY_UP ? '^' : ' '),
9517               (action & JOY_DOWN ? 'v' : ' '),
9518               (action & JOY_BUTTON_1 ? '1' : ' '),
9519               (action & JOY_BUTTON_2 ? '2' : ' '));
9520       }
9521     }
9522
9523     Print("(%03d) ", tape->pos[i].delay);
9524     Print("[%05d]\n", tape_frame_counter);
9525
9526     tape_frame_counter += tape->pos[i].delay;
9527   }
9528
9529   PrintLine("-", 79);
9530 }
9531
9532 void DumpTapes(void)
9533 {
9534   static LevelDirTree *dumptape_leveldir = NULL;
9535
9536   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9537                                                 global.dumptape_leveldir);
9538
9539   if (dumptape_leveldir == NULL)
9540     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9541
9542   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9543       global.dumptape_level_nr > dumptape_leveldir->last_level)
9544     Fail("no such level number: %d", global.dumptape_level_nr);
9545
9546   leveldir_current = dumptape_leveldir;
9547
9548   if (options.mytapes)
9549     LoadTape(global.dumptape_level_nr);
9550   else
9551     LoadSolutionTape(global.dumptape_level_nr);
9552
9553   DumpTape(&tape);
9554
9555   CloseAllAndExit(0);
9556 }
9557
9558
9559 // ============================================================================
9560 // score file functions
9561 // ============================================================================
9562
9563 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9564 {
9565   int i;
9566
9567   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9568   {
9569     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9570     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9571     scores->entry[i].score = 0;
9572     scores->entry[i].time = 0;
9573
9574     scores->entry[i].id = -1;
9575     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9576     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9577     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9578     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9579     strcpy(scores->entry[i].country_code, "??");
9580   }
9581
9582   scores->num_entries = 0;
9583   scores->last_added = -1;
9584   scores->last_added_local = -1;
9585
9586   scores->updated = FALSE;
9587   scores->uploaded = FALSE;
9588   scores->tape_downloaded = FALSE;
9589   scores->force_last_added = FALSE;
9590
9591   // The following values are intentionally not reset here:
9592   // - last_level_nr
9593   // - last_entry_nr
9594   // - next_level_nr
9595   // - continue_playing
9596   // - continue_on_return
9597 }
9598
9599 static void setScoreInfoToDefaults(void)
9600 {
9601   setScoreInfoToDefaultsExt(&scores);
9602 }
9603
9604 static void setServerScoreInfoToDefaults(void)
9605 {
9606   setScoreInfoToDefaultsExt(&server_scores);
9607 }
9608
9609 static void LoadScore_OLD(int nr)
9610 {
9611   int i;
9612   char *filename = getScoreFilename(nr);
9613   char cookie[MAX_LINE_LEN];
9614   char line[MAX_LINE_LEN];
9615   char *line_ptr;
9616   FILE *file;
9617
9618   if (!(file = fopen(filename, MODE_READ)))
9619     return;
9620
9621   // check file identifier
9622   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9623     cookie[0] = '\0';
9624   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9625     cookie[strlen(cookie) - 1] = '\0';
9626
9627   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9628   {
9629     Warn("unknown format of score file '%s'", filename);
9630
9631     fclose(file);
9632
9633     return;
9634   }
9635
9636   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9637   {
9638     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9639       Warn("fscanf() failed; %s", strerror(errno));
9640
9641     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9642       line[0] = '\0';
9643
9644     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9645       line[strlen(line) - 1] = '\0';
9646
9647     for (line_ptr = line; *line_ptr; line_ptr++)
9648     {
9649       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9650       {
9651         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9652         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9653         break;
9654       }
9655     }
9656   }
9657
9658   fclose(file);
9659 }
9660
9661 static void ConvertScore_OLD(void)
9662 {
9663   // only convert score to time for levels that rate playing time over score
9664   if (!level.rate_time_over_score)
9665     return;
9666
9667   // convert old score to playing time for score-less levels (like Supaplex)
9668   int time_final_max = 999;
9669   int i;
9670
9671   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9672   {
9673     int score = scores.entry[i].score;
9674
9675     if (score > 0 && score < time_final_max)
9676       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9677   }
9678 }
9679
9680 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9681 {
9682   scores->file_version = getFileVersion(file);
9683   scores->game_version = getFileVersion(file);
9684
9685   return chunk_size;
9686 }
9687
9688 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9689 {
9690   char *level_identifier = NULL;
9691   int level_identifier_size;
9692   int i;
9693
9694   level_identifier_size = getFile16BitBE(file);
9695
9696   level_identifier = checked_malloc(level_identifier_size);
9697
9698   for (i = 0; i < level_identifier_size; i++)
9699     level_identifier[i] = getFile8Bit(file);
9700
9701   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9702   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9703
9704   checked_free(level_identifier);
9705
9706   scores->level_nr = getFile16BitBE(file);
9707   scores->num_entries = getFile16BitBE(file);
9708
9709   chunk_size = 2 + level_identifier_size + 2 + 2;
9710
9711   return chunk_size;
9712 }
9713
9714 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9715 {
9716   int i, j;
9717
9718   for (i = 0; i < scores->num_entries; i++)
9719   {
9720     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9721       scores->entry[i].name[j] = getFile8Bit(file);
9722
9723     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9724   }
9725
9726   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9727
9728   return chunk_size;
9729 }
9730
9731 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9732 {
9733   int i;
9734
9735   for (i = 0; i < scores->num_entries; i++)
9736     scores->entry[i].score = getFile16BitBE(file);
9737
9738   chunk_size = scores->num_entries * 2;
9739
9740   return chunk_size;
9741 }
9742
9743 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9744 {
9745   int i;
9746
9747   for (i = 0; i < scores->num_entries; i++)
9748     scores->entry[i].score = getFile32BitBE(file);
9749
9750   chunk_size = scores->num_entries * 4;
9751
9752   return chunk_size;
9753 }
9754
9755 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9756 {
9757   int i;
9758
9759   for (i = 0; i < scores->num_entries; i++)
9760     scores->entry[i].time = getFile32BitBE(file);
9761
9762   chunk_size = scores->num_entries * 4;
9763
9764   return chunk_size;
9765 }
9766
9767 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9768 {
9769   int i, j;
9770
9771   for (i = 0; i < scores->num_entries; i++)
9772   {
9773     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9774       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9775
9776     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9777   }
9778
9779   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9780
9781   return chunk_size;
9782 }
9783
9784 void LoadScore(int nr)
9785 {
9786   char *filename = getScoreFilename(nr);
9787   char cookie[MAX_LINE_LEN];
9788   char chunk_name[CHUNK_ID_LEN + 1];
9789   int chunk_size;
9790   boolean old_score_file_format = FALSE;
9791   File *file;
9792
9793   // always start with reliable default values
9794   setScoreInfoToDefaults();
9795
9796   if (!(file = openFile(filename, MODE_READ)))
9797     return;
9798
9799   getFileChunkBE(file, chunk_name, NULL);
9800   if (strEqual(chunk_name, "RND1"))
9801   {
9802     getFile32BitBE(file);               // not used
9803
9804     getFileChunkBE(file, chunk_name, NULL);
9805     if (!strEqual(chunk_name, "SCOR"))
9806     {
9807       Warn("unknown format of score file '%s'", filename);
9808
9809       closeFile(file);
9810
9811       return;
9812     }
9813   }
9814   else  // check for old file format with cookie string
9815   {
9816     strcpy(cookie, chunk_name);
9817     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9818       cookie[4] = '\0';
9819     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9820       cookie[strlen(cookie) - 1] = '\0';
9821
9822     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9823     {
9824       Warn("unknown format of score file '%s'", filename);
9825
9826       closeFile(file);
9827
9828       return;
9829     }
9830
9831     old_score_file_format = TRUE;
9832   }
9833
9834   if (old_score_file_format)
9835   {
9836     // score files from versions before 4.2.4.0 without chunk structure
9837     LoadScore_OLD(nr);
9838
9839     // convert score to time, if possible (mainly for Supaplex levels)
9840     ConvertScore_OLD();
9841   }
9842   else
9843   {
9844     static struct
9845     {
9846       char *name;
9847       int size;
9848       int (*loader)(File *, int, struct ScoreInfo *);
9849     }
9850     chunk_info[] =
9851     {
9852       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9853       { "INFO", -1,                     LoadScore_INFO },
9854       { "NAME", -1,                     LoadScore_NAME },
9855       { "SCOR", -1,                     LoadScore_SCOR },
9856       { "SC4R", -1,                     LoadScore_SC4R },
9857       { "TIME", -1,                     LoadScore_TIME },
9858       { "TAPE", -1,                     LoadScore_TAPE },
9859
9860       {  NULL,  0,                      NULL }
9861     };
9862
9863     while (getFileChunkBE(file, chunk_name, &chunk_size))
9864     {
9865       int i = 0;
9866
9867       while (chunk_info[i].name != NULL &&
9868              !strEqual(chunk_name, chunk_info[i].name))
9869         i++;
9870
9871       if (chunk_info[i].name == NULL)
9872       {
9873         Warn("unknown chunk '%s' in score file '%s'",
9874               chunk_name, filename);
9875
9876         ReadUnusedBytesFromFile(file, chunk_size);
9877       }
9878       else if (chunk_info[i].size != -1 &&
9879                chunk_info[i].size != chunk_size)
9880       {
9881         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9882               chunk_size, chunk_name, filename);
9883
9884         ReadUnusedBytesFromFile(file, chunk_size);
9885       }
9886       else
9887       {
9888         // call function to load this score chunk
9889         int chunk_size_expected =
9890           (chunk_info[i].loader)(file, chunk_size, &scores);
9891
9892         // the size of some chunks cannot be checked before reading other
9893         // chunks first (like "HEAD" and "BODY") that contain some header
9894         // information, so check them here
9895         if (chunk_size_expected != chunk_size)
9896         {
9897           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9898                 chunk_size, chunk_name, filename);
9899         }
9900       }
9901     }
9902   }
9903
9904   closeFile(file);
9905 }
9906
9907 #if ENABLE_HISTORIC_CHUNKS
9908 void SaveScore_OLD(int nr)
9909 {
9910   int i;
9911   char *filename = getScoreFilename(nr);
9912   FILE *file;
9913
9914   // used instead of "leveldir_current->subdir" (for network games)
9915   InitScoreDirectory(levelset.identifier);
9916
9917   if (!(file = fopen(filename, MODE_WRITE)))
9918   {
9919     Warn("cannot save score for level %d", nr);
9920
9921     return;
9922   }
9923
9924   fprintf(file, "%s\n\n", SCORE_COOKIE);
9925
9926   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9927     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9928
9929   fclose(file);
9930
9931   SetFilePermissions(filename, PERMS_PRIVATE);
9932 }
9933 #endif
9934
9935 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9936 {
9937   putFileVersion(file, scores->file_version);
9938   putFileVersion(file, scores->game_version);
9939 }
9940
9941 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9942 {
9943   int level_identifier_size = strlen(scores->level_identifier) + 1;
9944   int i;
9945
9946   putFile16BitBE(file, level_identifier_size);
9947
9948   for (i = 0; i < level_identifier_size; i++)
9949     putFile8Bit(file, scores->level_identifier[i]);
9950
9951   putFile16BitBE(file, scores->level_nr);
9952   putFile16BitBE(file, scores->num_entries);
9953 }
9954
9955 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9956 {
9957   int i, j;
9958
9959   for (i = 0; i < scores->num_entries; i++)
9960   {
9961     int name_size = strlen(scores->entry[i].name);
9962
9963     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9964       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
9965   }
9966 }
9967
9968 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
9969 {
9970   int i;
9971
9972   for (i = 0; i < scores->num_entries; i++)
9973     putFile16BitBE(file, scores->entry[i].score);
9974 }
9975
9976 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
9977 {
9978   int i;
9979
9980   for (i = 0; i < scores->num_entries; i++)
9981     putFile32BitBE(file, scores->entry[i].score);
9982 }
9983
9984 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
9985 {
9986   int i;
9987
9988   for (i = 0; i < scores->num_entries; i++)
9989     putFile32BitBE(file, scores->entry[i].time);
9990 }
9991
9992 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
9993 {
9994   int i, j;
9995
9996   for (i = 0; i < scores->num_entries; i++)
9997   {
9998     int size = strlen(scores->entry[i].tape_basename);
9999
10000     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10001       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10002   }
10003 }
10004
10005 static void SaveScoreToFilename(char *filename)
10006 {
10007   FILE *file;
10008   int info_chunk_size;
10009   int name_chunk_size;
10010   int scor_chunk_size;
10011   int sc4r_chunk_size;
10012   int time_chunk_size;
10013   int tape_chunk_size;
10014   boolean has_large_score_values;
10015   int i;
10016
10017   if (!(file = fopen(filename, MODE_WRITE)))
10018   {
10019     Warn("cannot save score file '%s'", filename);
10020
10021     return;
10022   }
10023
10024   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10025   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10026   scor_chunk_size = scores.num_entries * 2;
10027   sc4r_chunk_size = scores.num_entries * 4;
10028   time_chunk_size = scores.num_entries * 4;
10029   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10030
10031   has_large_score_values = FALSE;
10032   for (i = 0; i < scores.num_entries; i++)
10033     if (scores.entry[i].score > 0xffff)
10034       has_large_score_values = TRUE;
10035
10036   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10037   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10038
10039   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10040   SaveScore_VERS(file, &scores);
10041
10042   putFileChunkBE(file, "INFO", info_chunk_size);
10043   SaveScore_INFO(file, &scores);
10044
10045   putFileChunkBE(file, "NAME", name_chunk_size);
10046   SaveScore_NAME(file, &scores);
10047
10048   if (has_large_score_values)
10049   {
10050     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10051     SaveScore_SC4R(file, &scores);
10052   }
10053   else
10054   {
10055     putFileChunkBE(file, "SCOR", scor_chunk_size);
10056     SaveScore_SCOR(file, &scores);
10057   }
10058
10059   putFileChunkBE(file, "TIME", time_chunk_size);
10060   SaveScore_TIME(file, &scores);
10061
10062   putFileChunkBE(file, "TAPE", tape_chunk_size);
10063   SaveScore_TAPE(file, &scores);
10064
10065   fclose(file);
10066
10067   SetFilePermissions(filename, PERMS_PRIVATE);
10068 }
10069
10070 void SaveScore(int nr)
10071 {
10072   char *filename = getScoreFilename(nr);
10073   int i;
10074
10075   // used instead of "leveldir_current->subdir" (for network games)
10076   InitScoreDirectory(levelset.identifier);
10077
10078   scores.file_version = FILE_VERSION_ACTUAL;
10079   scores.game_version = GAME_VERSION_ACTUAL;
10080
10081   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10082   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10083   scores.level_nr = level_nr;
10084
10085   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10086     if (scores.entry[i].score == 0 &&
10087         scores.entry[i].time == 0 &&
10088         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10089       break;
10090
10091   scores.num_entries = i;
10092
10093   if (scores.num_entries == 0)
10094     return;
10095
10096   SaveScoreToFilename(filename);
10097 }
10098
10099 static void LoadServerScoreFromCache(int nr)
10100 {
10101   struct ScoreEntry score_entry;
10102   struct
10103   {
10104     void *value;
10105     boolean is_string;
10106     int string_size;
10107   }
10108   score_mapping[] =
10109   {
10110     { &score_entry.score,               FALSE,  0                       },
10111     { &score_entry.time,                FALSE,  0                       },
10112     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10113     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10114     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10115     { &score_entry.id,                  FALSE,  0                       },
10116     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10117     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10118     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10119     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10120
10121     { NULL,                             FALSE,  0                       }
10122   };
10123   char *filename = getScoreCacheFilename(nr);
10124   SetupFileHash *score_hash = loadSetupFileHash(filename);
10125   int i, j;
10126
10127   server_scores.num_entries = 0;
10128
10129   if (score_hash == NULL)
10130     return;
10131
10132   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10133   {
10134     score_entry = server_scores.entry[i];
10135
10136     for (j = 0; score_mapping[j].value != NULL; j++)
10137     {
10138       char token[10];
10139
10140       sprintf(token, "%02d.%d", i, j);
10141
10142       char *value = getHashEntry(score_hash, token);
10143
10144       if (value == NULL)
10145         continue;
10146
10147       if (score_mapping[j].is_string)
10148       {
10149         char *score_value = (char *)score_mapping[j].value;
10150         int value_size = score_mapping[j].string_size;
10151
10152         strncpy(score_value, value, value_size);
10153         score_value[value_size] = '\0';
10154       }
10155       else
10156       {
10157         int *score_value = (int *)score_mapping[j].value;
10158
10159         *score_value = atoi(value);
10160       }
10161
10162       server_scores.num_entries = i + 1;
10163     }
10164
10165     server_scores.entry[i] = score_entry;
10166   }
10167
10168   freeSetupFileHash(score_hash);
10169 }
10170
10171 void LoadServerScore(int nr, boolean download_score)
10172 {
10173   if (!setup.use_api_server)
10174     return;
10175
10176   // always start with reliable default values
10177   setServerScoreInfoToDefaults();
10178
10179   // 1st step: load server scores from cache file (which may not exist)
10180   // (this should prevent reading it while the thread is writing to it)
10181   LoadServerScoreFromCache(nr);
10182
10183   if (download_score && runtime.use_api_server)
10184   {
10185     // 2nd step: download server scores from score server to cache file
10186     // (as thread, as it might time out if the server is not reachable)
10187     ApiGetScoreAsThread(nr);
10188   }
10189 }
10190
10191 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10192 {
10193   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10194
10195   // if score tape not uploaded, ask for uploading missing tapes later
10196   if (!setup.has_remaining_tapes)
10197     setup.ask_for_remaining_tapes = TRUE;
10198
10199   setup.provide_uploading_tapes = TRUE;
10200   setup.has_remaining_tapes = TRUE;
10201
10202   SaveSetup_ServerSetup();
10203 }
10204
10205 void SaveServerScore(int nr, boolean tape_saved)
10206 {
10207   if (!runtime.use_api_server)
10208   {
10209     PrepareScoreTapesForUpload(leveldir_current->subdir);
10210
10211     return;
10212   }
10213
10214   ApiAddScoreAsThread(nr, tape_saved, NULL);
10215 }
10216
10217 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10218                              char *score_tape_filename)
10219 {
10220   if (!runtime.use_api_server)
10221     return;
10222
10223   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10224 }
10225
10226 void LoadLocalAndServerScore(int nr, boolean download_score)
10227 {
10228   int last_added_local = scores.last_added_local;
10229   boolean force_last_added = scores.force_last_added;
10230
10231   // needed if only showing server scores
10232   setScoreInfoToDefaults();
10233
10234   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10235     LoadScore(nr);
10236
10237   // restore last added local score entry (before merging server scores)
10238   scores.last_added = scores.last_added_local = last_added_local;
10239
10240   if (setup.use_api_server &&
10241       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10242   {
10243     // load server scores from cache file and trigger update from server
10244     LoadServerScore(nr, download_score);
10245
10246     // merge local scores with scores from server
10247     MergeServerScore();
10248   }
10249
10250   if (force_last_added)
10251     scores.force_last_added = force_last_added;
10252 }
10253
10254
10255 // ============================================================================
10256 // setup file functions
10257 // ============================================================================
10258
10259 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10260
10261
10262 static struct TokenInfo global_setup_tokens[] =
10263 {
10264   {
10265     TYPE_STRING,
10266     &setup.player_name,                         "player_name"
10267   },
10268   {
10269     TYPE_SWITCH,
10270     &setup.multiple_users,                      "multiple_users"
10271   },
10272   {
10273     TYPE_SWITCH,
10274     &setup.sound,                               "sound"
10275   },
10276   {
10277     TYPE_SWITCH,
10278     &setup.sound_loops,                         "repeating_sound_loops"
10279   },
10280   {
10281     TYPE_SWITCH,
10282     &setup.sound_music,                         "background_music"
10283   },
10284   {
10285     TYPE_SWITCH,
10286     &setup.sound_simple,                        "simple_sound_effects"
10287   },
10288   {
10289     TYPE_SWITCH,
10290     &setup.toons,                               "toons"
10291   },
10292   {
10293     TYPE_SWITCH,
10294     &setup.global_animations,                   "global_animations"
10295   },
10296   {
10297     TYPE_SWITCH,
10298     &setup.scroll_delay,                        "scroll_delay"
10299   },
10300   {
10301     TYPE_SWITCH,
10302     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10303   },
10304   {
10305     TYPE_INTEGER,
10306     &setup.scroll_delay_value,                  "scroll_delay_value"
10307   },
10308   {
10309     TYPE_STRING,
10310     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10311   },
10312   {
10313     TYPE_INTEGER,
10314     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10315   },
10316   {
10317     TYPE_SWITCH,
10318     &setup.fade_screens,                        "fade_screens"
10319   },
10320   {
10321     TYPE_SWITCH,
10322     &setup.autorecord,                          "automatic_tape_recording"
10323   },
10324   {
10325     TYPE_SWITCH,
10326     &setup.autorecord_after_replay,             "autorecord_after_replay"
10327   },
10328   {
10329     TYPE_SWITCH,
10330     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10331   },
10332   {
10333     TYPE_SWITCH,
10334     &setup.show_titlescreen,                    "show_titlescreen"
10335   },
10336   {
10337     TYPE_SWITCH,
10338     &setup.quick_doors,                         "quick_doors"
10339   },
10340   {
10341     TYPE_SWITCH,
10342     &setup.team_mode,                           "team_mode"
10343   },
10344   {
10345     TYPE_SWITCH,
10346     &setup.handicap,                            "handicap"
10347   },
10348   {
10349     TYPE_SWITCH,
10350     &setup.skip_levels,                         "skip_levels"
10351   },
10352   {
10353     TYPE_SWITCH,
10354     &setup.increment_levels,                    "increment_levels"
10355   },
10356   {
10357     TYPE_SWITCH,
10358     &setup.auto_play_next_level,                "auto_play_next_level"
10359   },
10360   {
10361     TYPE_SWITCH,
10362     &setup.count_score_after_game,              "count_score_after_game"
10363   },
10364   {
10365     TYPE_SWITCH,
10366     &setup.show_scores_after_game,              "show_scores_after_game"
10367   },
10368   {
10369     TYPE_SWITCH,
10370     &setup.time_limit,                          "time_limit"
10371   },
10372   {
10373     TYPE_SWITCH,
10374     &setup.fullscreen,                          "fullscreen"
10375   },
10376   {
10377     TYPE_INTEGER,
10378     &setup.window_scaling_percent,              "window_scaling_percent"
10379   },
10380   {
10381     TYPE_STRING,
10382     &setup.window_scaling_quality,              "window_scaling_quality"
10383   },
10384   {
10385     TYPE_STRING,
10386     &setup.screen_rendering_mode,               "screen_rendering_mode"
10387   },
10388   {
10389     TYPE_STRING,
10390     &setup.vsync_mode,                          "vsync_mode"
10391   },
10392   {
10393     TYPE_SWITCH,
10394     &setup.ask_on_escape,                       "ask_on_escape"
10395   },
10396   {
10397     TYPE_SWITCH,
10398     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10399   },
10400   {
10401     TYPE_SWITCH,
10402     &setup.ask_on_game_over,                    "ask_on_game_over"
10403   },
10404   {
10405     TYPE_SWITCH,
10406     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10407   },
10408   {
10409     TYPE_SWITCH,
10410     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10411   },
10412   {
10413     TYPE_SWITCH,
10414     &setup.quick_switch,                        "quick_player_switch"
10415   },
10416   {
10417     TYPE_SWITCH,
10418     &setup.input_on_focus,                      "input_on_focus"
10419   },
10420   {
10421     TYPE_SWITCH,
10422     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10423   },
10424   {
10425     TYPE_SWITCH,
10426     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10427   },
10428   {
10429     TYPE_SWITCH,
10430     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10431   },
10432   {
10433     TYPE_SWITCH,
10434     &setup.game_speed_extended,                 "game_speed_extended"
10435   },
10436   {
10437     TYPE_INTEGER,
10438     &setup.game_frame_delay,                    "game_frame_delay"
10439   },
10440   {
10441     TYPE_SWITCH,
10442     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10443   },
10444   {
10445     TYPE_SWITCH,
10446     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10447   },
10448   {
10449     TYPE_SWITCH,
10450     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10451   },
10452   {
10453     TYPE_SWITCH3,
10454     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10455   },
10456   {
10457     TYPE_SWITCH,
10458     &setup.sp_show_border_elements,             "sp_show_border_elements"
10459   },
10460   {
10461     TYPE_SWITCH,
10462     &setup.small_game_graphics,                 "small_game_graphics"
10463   },
10464   {
10465     TYPE_SWITCH,
10466     &setup.show_load_save_buttons,              "show_load_save_buttons"
10467   },
10468   {
10469     TYPE_SWITCH,
10470     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10471   },
10472   {
10473     TYPE_STRING,
10474     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10475   },
10476   {
10477     TYPE_STRING,
10478     &setup.graphics_set,                        "graphics_set"
10479   },
10480   {
10481     TYPE_STRING,
10482     &setup.sounds_set,                          "sounds_set"
10483   },
10484   {
10485     TYPE_STRING,
10486     &setup.music_set,                           "music_set"
10487   },
10488   {
10489     TYPE_SWITCH3,
10490     &setup.override_level_graphics,             "override_level_graphics"
10491   },
10492   {
10493     TYPE_SWITCH3,
10494     &setup.override_level_sounds,               "override_level_sounds"
10495   },
10496   {
10497     TYPE_SWITCH3,
10498     &setup.override_level_music,                "override_level_music"
10499   },
10500   {
10501     TYPE_INTEGER,
10502     &setup.volume_simple,                       "volume_simple"
10503   },
10504   {
10505     TYPE_INTEGER,
10506     &setup.volume_loops,                        "volume_loops"
10507   },
10508   {
10509     TYPE_INTEGER,
10510     &setup.volume_music,                        "volume_music"
10511   },
10512   {
10513     TYPE_SWITCH,
10514     &setup.network_mode,                        "network_mode"
10515   },
10516   {
10517     TYPE_PLAYER,
10518     &setup.network_player_nr,                   "network_player"
10519   },
10520   {
10521     TYPE_STRING,
10522     &setup.network_server_hostname,             "network_server_hostname"
10523   },
10524   {
10525     TYPE_STRING,
10526     &setup.touch.control_type,                  "touch.control_type"
10527   },
10528   {
10529     TYPE_INTEGER,
10530     &setup.touch.move_distance,                 "touch.move_distance"
10531   },
10532   {
10533     TYPE_INTEGER,
10534     &setup.touch.drop_distance,                 "touch.drop_distance"
10535   },
10536   {
10537     TYPE_INTEGER,
10538     &setup.touch.transparency,                  "touch.transparency"
10539   },
10540   {
10541     TYPE_INTEGER,
10542     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10543   },
10544   {
10545     TYPE_INTEGER,
10546     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10547   },
10548   {
10549     TYPE_INTEGER,
10550     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10551   },
10552   {
10553     TYPE_INTEGER,
10554     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10555   },
10556   {
10557     TYPE_INTEGER,
10558     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10559   },
10560   {
10561     TYPE_INTEGER,
10562     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10563   },
10564   {
10565     TYPE_SWITCH,
10566     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10567   },
10568 };
10569
10570 static struct TokenInfo auto_setup_tokens[] =
10571 {
10572   {
10573     TYPE_INTEGER,
10574     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10575   },
10576 };
10577
10578 static struct TokenInfo server_setup_tokens[] =
10579 {
10580   {
10581     TYPE_STRING,
10582     &setup.player_uuid,                         "player_uuid"
10583   },
10584   {
10585     TYPE_INTEGER,
10586     &setup.player_version,                      "player_version"
10587   },
10588   {
10589     TYPE_SWITCH,
10590     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10591   },
10592   {
10593     TYPE_STRING,
10594     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10595   },
10596   {
10597     TYPE_STRING,
10598     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10599   },
10600   {
10601     TYPE_SWITCH,
10602     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10603   },
10604   {
10605     TYPE_SWITCH,
10606     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10607   },
10608   {
10609     TYPE_SWITCH,
10610     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10611   },
10612   {
10613     TYPE_SWITCH,
10614     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10615   },
10616   {
10617     TYPE_SWITCH,
10618     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10619   },
10620 };
10621
10622 static struct TokenInfo editor_setup_tokens[] =
10623 {
10624   {
10625     TYPE_SWITCH,
10626     &setup.editor.el_classic,                   "editor.el_classic"
10627   },
10628   {
10629     TYPE_SWITCH,
10630     &setup.editor.el_custom,                    "editor.el_custom"
10631   },
10632   {
10633     TYPE_SWITCH,
10634     &setup.editor.el_user_defined,              "editor.el_user_defined"
10635   },
10636   {
10637     TYPE_SWITCH,
10638     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10639   },
10640   {
10641     TYPE_SWITCH,
10642     &setup.editor.el_headlines,                 "editor.el_headlines"
10643   },
10644   {
10645     TYPE_SWITCH,
10646     &setup.editor.show_element_token,           "editor.show_element_token"
10647   },
10648   {
10649     TYPE_SWITCH,
10650     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10651   },
10652 };
10653
10654 static struct TokenInfo editor_cascade_setup_tokens[] =
10655 {
10656   {
10657     TYPE_SWITCH,
10658     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10659   },
10660   {
10661     TYPE_SWITCH,
10662     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10663   },
10664   {
10665     TYPE_SWITCH,
10666     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10667   },
10668   {
10669     TYPE_SWITCH,
10670     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10671   },
10672   {
10673     TYPE_SWITCH,
10674     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10675   },
10676   {
10677     TYPE_SWITCH,
10678     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10679   },
10680   {
10681     TYPE_SWITCH,
10682     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10683   },
10684   {
10685     TYPE_SWITCH,
10686     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10687   },
10688   {
10689     TYPE_SWITCH,
10690     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10691   },
10692   {
10693     TYPE_SWITCH,
10694     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10695   },
10696   {
10697     TYPE_SWITCH,
10698     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10699   },
10700   {
10701     TYPE_SWITCH,
10702     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10703   },
10704   {
10705     TYPE_SWITCH,
10706     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10707   },
10708   {
10709     TYPE_SWITCH,
10710     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10711   },
10712   {
10713     TYPE_SWITCH,
10714     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10715   },
10716   {
10717     TYPE_SWITCH,
10718     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10719   },
10720   {
10721     TYPE_SWITCH,
10722     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10723   },
10724   {
10725     TYPE_SWITCH,
10726     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10727   },
10728   {
10729     TYPE_SWITCH,
10730     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10731   },
10732   {
10733     TYPE_SWITCH,
10734     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10735   },
10736 };
10737
10738 static struct TokenInfo shortcut_setup_tokens[] =
10739 {
10740   {
10741     TYPE_KEY_X11,
10742     &setup.shortcut.save_game,                  "shortcut.save_game"
10743   },
10744   {
10745     TYPE_KEY_X11,
10746     &setup.shortcut.load_game,                  "shortcut.load_game"
10747   },
10748   {
10749     TYPE_KEY_X11,
10750     &setup.shortcut.restart_game,               "shortcut.restart_game"
10751   },
10752   {
10753     TYPE_KEY_X11,
10754     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10755   },
10756   {
10757     TYPE_KEY_X11,
10758     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10759   },
10760   {
10761     TYPE_KEY_X11,
10762     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10763   },
10764   {
10765     TYPE_KEY_X11,
10766     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10767   },
10768   {
10769     TYPE_KEY_X11,
10770     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10771   },
10772   {
10773     TYPE_KEY_X11,
10774     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10775   },
10776   {
10777     TYPE_KEY_X11,
10778     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10779   },
10780   {
10781     TYPE_KEY_X11,
10782     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10783   },
10784   {
10785     TYPE_KEY_X11,
10786     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10787   },
10788   {
10789     TYPE_KEY_X11,
10790     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10791   },
10792   {
10793     TYPE_KEY_X11,
10794     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10795   },
10796   {
10797     TYPE_KEY_X11,
10798     &setup.shortcut.tape_record,                "shortcut.tape_record"
10799   },
10800   {
10801     TYPE_KEY_X11,
10802     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10803   },
10804   {
10805     TYPE_KEY_X11,
10806     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10807   },
10808   {
10809     TYPE_KEY_X11,
10810     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10811   },
10812   {
10813     TYPE_KEY_X11,
10814     &setup.shortcut.sound_music,                "shortcut.sound_music"
10815   },
10816   {
10817     TYPE_KEY_X11,
10818     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10819   },
10820   {
10821     TYPE_KEY_X11,
10822     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10823   },
10824   {
10825     TYPE_KEY_X11,
10826     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10827   },
10828   {
10829     TYPE_KEY_X11,
10830     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10831   },
10832 };
10833
10834 static struct SetupInputInfo setup_input;
10835 static struct TokenInfo player_setup_tokens[] =
10836 {
10837   {
10838     TYPE_BOOLEAN,
10839     &setup_input.use_joystick,                  ".use_joystick"
10840   },
10841   {
10842     TYPE_STRING,
10843     &setup_input.joy.device_name,               ".joy.device_name"
10844   },
10845   {
10846     TYPE_INTEGER,
10847     &setup_input.joy.xleft,                     ".joy.xleft"
10848   },
10849   {
10850     TYPE_INTEGER,
10851     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10852   },
10853   {
10854     TYPE_INTEGER,
10855     &setup_input.joy.xright,                    ".joy.xright"
10856   },
10857   {
10858     TYPE_INTEGER,
10859     &setup_input.joy.yupper,                    ".joy.yupper"
10860   },
10861   {
10862     TYPE_INTEGER,
10863     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10864   },
10865   {
10866     TYPE_INTEGER,
10867     &setup_input.joy.ylower,                    ".joy.ylower"
10868   },
10869   {
10870     TYPE_INTEGER,
10871     &setup_input.joy.snap,                      ".joy.snap_field"
10872   },
10873   {
10874     TYPE_INTEGER,
10875     &setup_input.joy.drop,                      ".joy.place_bomb"
10876   },
10877   {
10878     TYPE_KEY_X11,
10879     &setup_input.key.left,                      ".key.move_left"
10880   },
10881   {
10882     TYPE_KEY_X11,
10883     &setup_input.key.right,                     ".key.move_right"
10884   },
10885   {
10886     TYPE_KEY_X11,
10887     &setup_input.key.up,                        ".key.move_up"
10888   },
10889   {
10890     TYPE_KEY_X11,
10891     &setup_input.key.down,                      ".key.move_down"
10892   },
10893   {
10894     TYPE_KEY_X11,
10895     &setup_input.key.snap,                      ".key.snap_field"
10896   },
10897   {
10898     TYPE_KEY_X11,
10899     &setup_input.key.drop,                      ".key.place_bomb"
10900   },
10901 };
10902
10903 static struct TokenInfo system_setup_tokens[] =
10904 {
10905   {
10906     TYPE_STRING,
10907     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10908   },
10909   {
10910     TYPE_STRING,
10911     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10912   },
10913   {
10914     TYPE_STRING,
10915     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10916   },
10917   {
10918     TYPE_INTEGER,
10919     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10920   },
10921 };
10922
10923 static struct TokenInfo internal_setup_tokens[] =
10924 {
10925   {
10926     TYPE_STRING,
10927     &setup.internal.program_title,              "program_title"
10928   },
10929   {
10930     TYPE_STRING,
10931     &setup.internal.program_version,            "program_version"
10932   },
10933   {
10934     TYPE_STRING,
10935     &setup.internal.program_author,             "program_author"
10936   },
10937   {
10938     TYPE_STRING,
10939     &setup.internal.program_email,              "program_email"
10940   },
10941   {
10942     TYPE_STRING,
10943     &setup.internal.program_website,            "program_website"
10944   },
10945   {
10946     TYPE_STRING,
10947     &setup.internal.program_copyright,          "program_copyright"
10948   },
10949   {
10950     TYPE_STRING,
10951     &setup.internal.program_company,            "program_company"
10952   },
10953   {
10954     TYPE_STRING,
10955     &setup.internal.program_icon_file,          "program_icon_file"
10956   },
10957   {
10958     TYPE_STRING,
10959     &setup.internal.default_graphics_set,       "default_graphics_set"
10960   },
10961   {
10962     TYPE_STRING,
10963     &setup.internal.default_sounds_set,         "default_sounds_set"
10964   },
10965   {
10966     TYPE_STRING,
10967     &setup.internal.default_music_set,          "default_music_set"
10968   },
10969   {
10970     TYPE_STRING,
10971     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
10972   },
10973   {
10974     TYPE_STRING,
10975     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
10976   },
10977   {
10978     TYPE_STRING,
10979     &setup.internal.fallback_music_file,        "fallback_music_file"
10980   },
10981   {
10982     TYPE_STRING,
10983     &setup.internal.default_level_series,       "default_level_series"
10984   },
10985   {
10986     TYPE_INTEGER,
10987     &setup.internal.default_window_width,       "default_window_width"
10988   },
10989   {
10990     TYPE_INTEGER,
10991     &setup.internal.default_window_height,      "default_window_height"
10992   },
10993   {
10994     TYPE_BOOLEAN,
10995     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
10996   },
10997   {
10998     TYPE_BOOLEAN,
10999     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11000   },
11001   {
11002     TYPE_BOOLEAN,
11003     &setup.internal.create_user_levelset,       "create_user_levelset"
11004   },
11005   {
11006     TYPE_BOOLEAN,
11007     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11008   },
11009   {
11010     TYPE_BOOLEAN,
11011     &setup.internal.menu_game,                  "menu_game"
11012   },
11013   {
11014     TYPE_BOOLEAN,
11015     &setup.internal.menu_engines,               "menu_engines"
11016   },
11017   {
11018     TYPE_BOOLEAN,
11019     &setup.internal.menu_editor,                "menu_editor"
11020   },
11021   {
11022     TYPE_BOOLEAN,
11023     &setup.internal.menu_graphics,              "menu_graphics"
11024   },
11025   {
11026     TYPE_BOOLEAN,
11027     &setup.internal.menu_sound,                 "menu_sound"
11028   },
11029   {
11030     TYPE_BOOLEAN,
11031     &setup.internal.menu_artwork,               "menu_artwork"
11032   },
11033   {
11034     TYPE_BOOLEAN,
11035     &setup.internal.menu_input,                 "menu_input"
11036   },
11037   {
11038     TYPE_BOOLEAN,
11039     &setup.internal.menu_touch,                 "menu_touch"
11040   },
11041   {
11042     TYPE_BOOLEAN,
11043     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11044   },
11045   {
11046     TYPE_BOOLEAN,
11047     &setup.internal.menu_exit,                  "menu_exit"
11048   },
11049   {
11050     TYPE_BOOLEAN,
11051     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11052   },
11053   {
11054     TYPE_BOOLEAN,
11055     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11056   },
11057   {
11058     TYPE_BOOLEAN,
11059     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11060   },
11061   {
11062     TYPE_BOOLEAN,
11063     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11064   },
11065   {
11066     TYPE_BOOLEAN,
11067     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11068   },
11069   {
11070     TYPE_BOOLEAN,
11071     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11072   },
11073   {
11074     TYPE_BOOLEAN,
11075     &setup.internal.info_title,                 "info_title"
11076   },
11077   {
11078     TYPE_BOOLEAN,
11079     &setup.internal.info_elements,              "info_elements"
11080   },
11081   {
11082     TYPE_BOOLEAN,
11083     &setup.internal.info_music,                 "info_music"
11084   },
11085   {
11086     TYPE_BOOLEAN,
11087     &setup.internal.info_credits,               "info_credits"
11088   },
11089   {
11090     TYPE_BOOLEAN,
11091     &setup.internal.info_program,               "info_program"
11092   },
11093   {
11094     TYPE_BOOLEAN,
11095     &setup.internal.info_version,               "info_version"
11096   },
11097   {
11098     TYPE_BOOLEAN,
11099     &setup.internal.info_levelset,              "info_levelset"
11100   },
11101   {
11102     TYPE_BOOLEAN,
11103     &setup.internal.info_exit,                  "info_exit"
11104   },
11105 };
11106
11107 static struct TokenInfo debug_setup_tokens[] =
11108 {
11109   {
11110     TYPE_INTEGER,
11111     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11112   },
11113   {
11114     TYPE_INTEGER,
11115     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11116   },
11117   {
11118     TYPE_INTEGER,
11119     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11120   },
11121   {
11122     TYPE_INTEGER,
11123     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11124   },
11125   {
11126     TYPE_INTEGER,
11127     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11128   },
11129   {
11130     TYPE_INTEGER,
11131     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11132   },
11133   {
11134     TYPE_INTEGER,
11135     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11136   },
11137   {
11138     TYPE_INTEGER,
11139     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11140   },
11141   {
11142     TYPE_INTEGER,
11143     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11144   },
11145   {
11146     TYPE_INTEGER,
11147     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11148   },
11149   {
11150     TYPE_KEY_X11,
11151     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11152   },
11153   {
11154     TYPE_KEY_X11,
11155     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11156   },
11157   {
11158     TYPE_KEY_X11,
11159     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11160   },
11161   {
11162     TYPE_KEY_X11,
11163     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11164   },
11165   {
11166     TYPE_KEY_X11,
11167     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11168   },
11169   {
11170     TYPE_KEY_X11,
11171     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11172   },
11173   {
11174     TYPE_KEY_X11,
11175     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11176   },
11177   {
11178     TYPE_KEY_X11,
11179     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11180   },
11181   {
11182     TYPE_KEY_X11,
11183     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11184   },
11185   {
11186     TYPE_KEY_X11,
11187     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11188   },
11189   {
11190     TYPE_BOOLEAN,
11191     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11192   {
11193     TYPE_BOOLEAN,
11194     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11195   },
11196   {
11197     TYPE_BOOLEAN,
11198     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11199   },
11200   {
11201     TYPE_SWITCH3,
11202     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11203   },
11204   {
11205     TYPE_INTEGER,
11206     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11207   },
11208 };
11209
11210 static struct TokenInfo options_setup_tokens[] =
11211 {
11212   {
11213     TYPE_BOOLEAN,
11214     &setup.options.verbose,                     "options.verbose"
11215   },
11216   {
11217     TYPE_BOOLEAN,
11218     &setup.options.debug,                       "options.debug"
11219   },
11220   {
11221     TYPE_STRING,
11222     &setup.options.debug_mode,                  "options.debug_mode"
11223   },
11224 };
11225
11226 static void setSetupInfoToDefaults(struct SetupInfo *si)
11227 {
11228   int i;
11229
11230   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11231
11232   si->multiple_users = TRUE;
11233
11234   si->sound = TRUE;
11235   si->sound_loops = TRUE;
11236   si->sound_music = TRUE;
11237   si->sound_simple = TRUE;
11238   si->toons = TRUE;
11239   si->global_animations = TRUE;
11240   si->scroll_delay = TRUE;
11241   si->forced_scroll_delay = FALSE;
11242   si->scroll_delay_value = STD_SCROLL_DELAY;
11243   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11244   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11245   si->fade_screens = TRUE;
11246   si->autorecord = TRUE;
11247   si->autorecord_after_replay = TRUE;
11248   si->auto_pause_on_start = FALSE;
11249   si->show_titlescreen = TRUE;
11250   si->quick_doors = FALSE;
11251   si->team_mode = FALSE;
11252   si->handicap = TRUE;
11253   si->skip_levels = TRUE;
11254   si->increment_levels = TRUE;
11255   si->auto_play_next_level = TRUE;
11256   si->count_score_after_game = TRUE;
11257   si->show_scores_after_game = TRUE;
11258   si->time_limit = TRUE;
11259   si->fullscreen = FALSE;
11260   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11261   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11262   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11263   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11264   si->ask_on_escape = TRUE;
11265   si->ask_on_escape_editor = TRUE;
11266   si->ask_on_game_over = TRUE;
11267   si->ask_on_quit_game = TRUE;
11268   si->ask_on_quit_program = TRUE;
11269   si->quick_switch = FALSE;
11270   si->input_on_focus = FALSE;
11271   si->prefer_aga_graphics = TRUE;
11272   si->prefer_lowpass_sounds = FALSE;
11273   si->prefer_extra_panel_items = TRUE;
11274   si->game_speed_extended = FALSE;
11275   si->game_frame_delay = GAME_FRAME_DELAY;
11276   si->bd_skip_uncovering = FALSE;
11277   si->bd_skip_hatching = FALSE;
11278   si->bd_scroll_delay = TRUE;
11279   si->bd_smooth_movements = AUTO;
11280   si->sp_show_border_elements = FALSE;
11281   si->small_game_graphics = FALSE;
11282   si->show_load_save_buttons = FALSE;
11283   si->show_undo_redo_buttons = FALSE;
11284   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11285
11286   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11287   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11288   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11289
11290   si->override_level_graphics = FALSE;
11291   si->override_level_sounds = FALSE;
11292   si->override_level_music = FALSE;
11293
11294   si->volume_simple = 100;              // percent
11295   si->volume_loops = 100;               // percent
11296   si->volume_music = 100;               // percent
11297
11298   si->network_mode = FALSE;
11299   si->network_player_nr = 0;            // first player
11300   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11301
11302   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11303   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11304   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11305   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11306   si->touch.draw_outlined = TRUE;
11307   si->touch.draw_pressed = TRUE;
11308
11309   for (i = 0; i < 2; i++)
11310   {
11311     char *default_grid_button[6][2] =
11312     {
11313       { "      ", "  ^^  " },
11314       { "      ", "  ^^  " },
11315       { "      ", "<<  >>" },
11316       { "      ", "<<  >>" },
11317       { "111222", "  vv  " },
11318       { "111222", "  vv  " }
11319     };
11320     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11321     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11322     int min_xsize = MIN(6, grid_xsize);
11323     int min_ysize = MIN(6, grid_ysize);
11324     int startx = grid_xsize - min_xsize;
11325     int starty = grid_ysize - min_ysize;
11326     int x, y;
11327
11328     // virtual buttons grid can only be set to defaults if video is initialized
11329     // (this will be repeated if virtual buttons are not loaded from setup file)
11330     if (video.initialized)
11331     {
11332       si->touch.grid_xsize[i] = grid_xsize;
11333       si->touch.grid_ysize[i] = grid_ysize;
11334     }
11335     else
11336     {
11337       si->touch.grid_xsize[i] = -1;
11338       si->touch.grid_ysize[i] = -1;
11339     }
11340
11341     for (x = 0; x < MAX_GRID_XSIZE; x++)
11342       for (y = 0; y < MAX_GRID_YSIZE; y++)
11343         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11344
11345     for (x = 0; x < min_xsize; x++)
11346       for (y = 0; y < min_ysize; y++)
11347         si->touch.grid_button[i][x][starty + y] =
11348           default_grid_button[y][0][x];
11349
11350     for (x = 0; x < min_xsize; x++)
11351       for (y = 0; y < min_ysize; y++)
11352         si->touch.grid_button[i][startx + x][starty + y] =
11353           default_grid_button[y][1][x];
11354   }
11355
11356   si->touch.grid_initialized            = video.initialized;
11357
11358   si->touch.overlay_buttons             = FALSE;
11359
11360   si->editor.el_boulderdash             = TRUE;
11361   si->editor.el_boulderdash_native      = TRUE;
11362   si->editor.el_boulderdash_effects     = TRUE;
11363   si->editor.el_emerald_mine            = TRUE;
11364   si->editor.el_emerald_mine_club       = TRUE;
11365   si->editor.el_more                    = TRUE;
11366   si->editor.el_sokoban                 = TRUE;
11367   si->editor.el_supaplex                = TRUE;
11368   si->editor.el_diamond_caves           = TRUE;
11369   si->editor.el_dx_boulderdash          = TRUE;
11370
11371   si->editor.el_mirror_magic            = TRUE;
11372   si->editor.el_deflektor               = TRUE;
11373
11374   si->editor.el_chars                   = TRUE;
11375   si->editor.el_steel_chars             = TRUE;
11376
11377   si->editor.el_classic                 = TRUE;
11378   si->editor.el_custom                  = TRUE;
11379
11380   si->editor.el_user_defined            = FALSE;
11381   si->editor.el_dynamic                 = TRUE;
11382
11383   si->editor.el_headlines               = TRUE;
11384
11385   si->editor.show_element_token         = FALSE;
11386
11387   si->editor.show_read_only_warning     = TRUE;
11388
11389   si->editor.use_template_for_new_levels = TRUE;
11390
11391   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11392   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11393   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11394   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11395   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11396
11397   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11398   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11399   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11400   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11401   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11402
11403   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11404   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11405   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11406   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11407   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11408   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11409
11410   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11411   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11412   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11413
11414   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11415   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11416   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11417   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11418
11419   for (i = 0; i < MAX_PLAYERS; i++)
11420   {
11421     si->input[i].use_joystick = FALSE;
11422     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11423     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11424     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11425     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11426     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11427     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11428     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11429     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11430     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11431     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11432     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11433     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11434     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11435     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11436     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11437   }
11438
11439   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11440   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11441   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11442   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11443
11444   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11445   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11446   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11447   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11448   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11449   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11450   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11451
11452   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11453
11454   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11455   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11456   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11457
11458   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11459   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11460   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11461
11462   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11463   si->internal.choose_from_top_leveldir = FALSE;
11464   si->internal.show_scaling_in_title = TRUE;
11465   si->internal.create_user_levelset = TRUE;
11466   si->internal.info_screens_from_main = FALSE;
11467
11468   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11469   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11470
11471   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11472   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11473   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11474   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11475   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11476   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11477   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11478   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11479   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11480   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11481
11482   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11483   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11484   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11485   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11486   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11487   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11488   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11489   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11490   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11491   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11492
11493   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11494   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11495
11496   si->debug.show_frames_per_second = FALSE;
11497
11498   si->debug.xsn_mode = AUTO;
11499   si->debug.xsn_percent = 0;
11500
11501   si->options.verbose = FALSE;
11502   si->options.debug = FALSE;
11503   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11504
11505 #if defined(PLATFORM_ANDROID)
11506   si->fullscreen = TRUE;
11507   si->touch.overlay_buttons = TRUE;
11508 #endif
11509
11510   setHideSetupEntry(&setup.debug.xsn_mode);
11511 }
11512
11513 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11514 {
11515   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11516 }
11517
11518 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11519 {
11520   si->player_uuid = NULL;       // (will be set later)
11521   si->player_version = 1;       // (will be set later)
11522
11523   si->use_api_server = TRUE;
11524   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11525   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11526   si->ask_for_uploading_tapes = TRUE;
11527   si->ask_for_remaining_tapes = FALSE;
11528   si->provide_uploading_tapes = TRUE;
11529   si->ask_for_using_api_server = TRUE;
11530   si->has_remaining_tapes = FALSE;
11531 }
11532
11533 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11534 {
11535   si->editor_cascade.el_bd              = TRUE;
11536   si->editor_cascade.el_bd_native       = TRUE;
11537   si->editor_cascade.el_bd_effects      = FALSE;
11538   si->editor_cascade.el_em              = TRUE;
11539   si->editor_cascade.el_emc             = TRUE;
11540   si->editor_cascade.el_rnd             = TRUE;
11541   si->editor_cascade.el_sb              = TRUE;
11542   si->editor_cascade.el_sp              = TRUE;
11543   si->editor_cascade.el_dc              = TRUE;
11544   si->editor_cascade.el_dx              = TRUE;
11545
11546   si->editor_cascade.el_mm              = TRUE;
11547   si->editor_cascade.el_df              = TRUE;
11548
11549   si->editor_cascade.el_chars           = FALSE;
11550   si->editor_cascade.el_steel_chars     = FALSE;
11551   si->editor_cascade.el_ce              = FALSE;
11552   si->editor_cascade.el_ge              = FALSE;
11553   si->editor_cascade.el_es              = FALSE;
11554   si->editor_cascade.el_ref             = FALSE;
11555   si->editor_cascade.el_user            = FALSE;
11556   si->editor_cascade.el_dynamic         = FALSE;
11557 }
11558
11559 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11560
11561 static char *getHideSetupToken(void *setup_value)
11562 {
11563   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11564
11565   if (setup_value != NULL)
11566     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11567
11568   return hide_setup_token;
11569 }
11570
11571 void setHideSetupEntry(void *setup_value)
11572 {
11573   char *hide_setup_token = getHideSetupToken(setup_value);
11574
11575   if (hide_setup_hash == NULL)
11576     hide_setup_hash = newSetupFileHash();
11577
11578   if (setup_value != NULL)
11579     setHashEntry(hide_setup_hash, hide_setup_token, "");
11580 }
11581
11582 void removeHideSetupEntry(void *setup_value)
11583 {
11584   char *hide_setup_token = getHideSetupToken(setup_value);
11585
11586   if (setup_value != NULL)
11587     removeHashEntry(hide_setup_hash, hide_setup_token);
11588 }
11589
11590 boolean hideSetupEntry(void *setup_value)
11591 {
11592   char *hide_setup_token = getHideSetupToken(setup_value);
11593
11594   return (setup_value != NULL &&
11595           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11596 }
11597
11598 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11599                                       struct TokenInfo *token_info,
11600                                       int token_nr, char *token_text)
11601 {
11602   char *token_hide_text = getStringCat2(token_text, ".hide");
11603   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11604
11605   // set the value of this setup option in the setup option structure
11606   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11607
11608   // check if this setup option should be hidden in the setup menu
11609   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11610     setHideSetupEntry(token_info[token_nr].value);
11611
11612   free(token_hide_text);
11613 }
11614
11615 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11616                                       struct TokenInfo *token_info,
11617                                       int token_nr)
11618 {
11619   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11620                             token_info[token_nr].text);
11621 }
11622
11623 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11624 {
11625   int i, pnr;
11626
11627   if (!setup_file_hash)
11628     return;
11629
11630   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11631     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11632
11633   setup.touch.grid_initialized = TRUE;
11634   for (i = 0; i < 2; i++)
11635   {
11636     int grid_xsize = setup.touch.grid_xsize[i];
11637     int grid_ysize = setup.touch.grid_ysize[i];
11638     int x, y;
11639
11640     // if virtual buttons are not loaded from setup file, repeat initializing
11641     // virtual buttons grid with default values later when video is initialized
11642     if (grid_xsize == -1 ||
11643         grid_ysize == -1)
11644     {
11645       setup.touch.grid_initialized = FALSE;
11646
11647       continue;
11648     }
11649
11650     for (y = 0; y < grid_ysize; y++)
11651     {
11652       char token_string[MAX_LINE_LEN];
11653
11654       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11655
11656       char *value_string = getHashEntry(setup_file_hash, token_string);
11657
11658       if (value_string == NULL)
11659         continue;
11660
11661       for (x = 0; x < grid_xsize; x++)
11662       {
11663         char c = value_string[x];
11664
11665         setup.touch.grid_button[i][x][y] =
11666           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11667       }
11668     }
11669   }
11670
11671   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11672     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11673
11674   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11675     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11676
11677   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11678   {
11679     char prefix[30];
11680
11681     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11682
11683     setup_input = setup.input[pnr];
11684     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11685     {
11686       char full_token[100];
11687
11688       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11689       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11690                                 full_token);
11691     }
11692     setup.input[pnr] = setup_input;
11693   }
11694
11695   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11696     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11697
11698   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11699     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11700
11701   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11702     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11703
11704   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11705     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11706
11707   setHideRelatedSetupEntries();
11708 }
11709
11710 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11711 {
11712   int i;
11713
11714   if (!setup_file_hash)
11715     return;
11716
11717   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11718     setSetupInfo(auto_setup_tokens, i,
11719                  getHashEntry(setup_file_hash,
11720                               auto_setup_tokens[i].text));
11721 }
11722
11723 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11724 {
11725   int i;
11726
11727   if (!setup_file_hash)
11728     return;
11729
11730   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11731     setSetupInfo(server_setup_tokens, i,
11732                  getHashEntry(setup_file_hash,
11733                               server_setup_tokens[i].text));
11734 }
11735
11736 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11737 {
11738   int i;
11739
11740   if (!setup_file_hash)
11741     return;
11742
11743   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11744     setSetupInfo(editor_cascade_setup_tokens, i,
11745                  getHashEntry(setup_file_hash,
11746                               editor_cascade_setup_tokens[i].text));
11747 }
11748
11749 void LoadUserNames(void)
11750 {
11751   int last_user_nr = user.nr;
11752   int i;
11753
11754   if (global.user_names != NULL)
11755   {
11756     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11757       checked_free(global.user_names[i]);
11758
11759     checked_free(global.user_names);
11760   }
11761
11762   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11763
11764   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11765   {
11766     user.nr = i;
11767
11768     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11769
11770     if (setup_file_hash)
11771     {
11772       char *player_name = getHashEntry(setup_file_hash, "player_name");
11773
11774       global.user_names[i] = getFixedUserName(player_name);
11775
11776       freeSetupFileHash(setup_file_hash);
11777     }
11778
11779     if (global.user_names[i] == NULL)
11780       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11781   }
11782
11783   user.nr = last_user_nr;
11784 }
11785
11786 void LoadSetupFromFilename(char *filename)
11787 {
11788   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11789
11790   if (setup_file_hash)
11791   {
11792     decodeSetupFileHash_Default(setup_file_hash);
11793
11794     freeSetupFileHash(setup_file_hash);
11795   }
11796   else
11797   {
11798     Debug("setup", "using default setup values");
11799   }
11800 }
11801
11802 static void LoadSetup_SpecialPostProcessing(void)
11803 {
11804   char *player_name_new;
11805
11806   // needed to work around problems with fixed length strings
11807   player_name_new = getFixedUserName(setup.player_name);
11808   free(setup.player_name);
11809   setup.player_name = player_name_new;
11810
11811   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11812   if (setup.scroll_delay == FALSE)
11813   {
11814     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11815     setup.scroll_delay = TRUE;                  // now always "on"
11816   }
11817
11818   // make sure that scroll delay value stays inside valid range
11819   setup.scroll_delay_value =
11820     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11821 }
11822
11823 void LoadSetup_Default(void)
11824 {
11825   char *filename;
11826
11827   // always start with reliable default values
11828   setSetupInfoToDefaults(&setup);
11829
11830   // try to load setup values from default setup file
11831   filename = getDefaultSetupFilename();
11832
11833   if (fileExists(filename))
11834     LoadSetupFromFilename(filename);
11835
11836   // try to load setup values from platform setup file
11837   filename = getPlatformSetupFilename();
11838
11839   if (fileExists(filename))
11840     LoadSetupFromFilename(filename);
11841
11842   // try to load setup values from user setup file
11843   filename = getSetupFilename();
11844
11845   LoadSetupFromFilename(filename);
11846
11847   LoadSetup_SpecialPostProcessing();
11848 }
11849
11850 void LoadSetup_AutoSetup(void)
11851 {
11852   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11853   SetupFileHash *setup_file_hash = NULL;
11854
11855   // always start with reliable default values
11856   setSetupInfoToDefaults_AutoSetup(&setup);
11857
11858   setup_file_hash = loadSetupFileHash(filename);
11859
11860   if (setup_file_hash)
11861   {
11862     decodeSetupFileHash_AutoSetup(setup_file_hash);
11863
11864     freeSetupFileHash(setup_file_hash);
11865   }
11866
11867   free(filename);
11868 }
11869
11870 void LoadSetup_ServerSetup(void)
11871 {
11872   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11873   SetupFileHash *setup_file_hash = NULL;
11874
11875   // always start with reliable default values
11876   setSetupInfoToDefaults_ServerSetup(&setup);
11877
11878   setup_file_hash = loadSetupFileHash(filename);
11879
11880   if (setup_file_hash)
11881   {
11882     decodeSetupFileHash_ServerSetup(setup_file_hash);
11883
11884     freeSetupFileHash(setup_file_hash);
11885   }
11886
11887   free(filename);
11888
11889   if (setup.player_uuid == NULL)
11890   {
11891     // player UUID does not yet exist in setup file
11892     setup.player_uuid = getStringCopy(getUUID());
11893     setup.player_version = 2;
11894
11895     SaveSetup_ServerSetup();
11896   }
11897 }
11898
11899 void LoadSetup_EditorCascade(void)
11900 {
11901   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11902   SetupFileHash *setup_file_hash = NULL;
11903
11904   // always start with reliable default values
11905   setSetupInfoToDefaults_EditorCascade(&setup);
11906
11907   setup_file_hash = loadSetupFileHash(filename);
11908
11909   if (setup_file_hash)
11910   {
11911     decodeSetupFileHash_EditorCascade(setup_file_hash);
11912
11913     freeSetupFileHash(setup_file_hash);
11914   }
11915
11916   free(filename);
11917 }
11918
11919 void LoadSetup(void)
11920 {
11921   LoadSetup_Default();
11922   LoadSetup_AutoSetup();
11923   LoadSetup_ServerSetup();
11924   LoadSetup_EditorCascade();
11925 }
11926
11927 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11928                                            char *mapping_line)
11929 {
11930   char mapping_guid[MAX_LINE_LEN];
11931   char *mapping_start, *mapping_end;
11932
11933   // get GUID from game controller mapping line: copy complete line
11934   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11935   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11936
11937   // get GUID from game controller mapping line: cut after GUID part
11938   mapping_start = strchr(mapping_guid, ',');
11939   if (mapping_start != NULL)
11940     *mapping_start = '\0';
11941
11942   // cut newline from game controller mapping line
11943   mapping_end = strchr(mapping_line, '\n');
11944   if (mapping_end != NULL)
11945     *mapping_end = '\0';
11946
11947   // add mapping entry to game controller mappings hash
11948   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11949 }
11950
11951 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11952                                                  char *filename)
11953 {
11954   FILE *file;
11955
11956   if (!(file = fopen(filename, MODE_READ)))
11957   {
11958     Warn("cannot read game controller mappings file '%s'", filename);
11959
11960     return;
11961   }
11962
11963   while (!feof(file))
11964   {
11965     char line[MAX_LINE_LEN];
11966
11967     if (!fgets(line, MAX_LINE_LEN, file))
11968       break;
11969
11970     addGameControllerMappingToHash(mappings_hash, line);
11971   }
11972
11973   fclose(file);
11974 }
11975
11976 void SaveSetup_Default(void)
11977 {
11978   char *filename = getSetupFilename();
11979   FILE *file;
11980   int i, pnr;
11981
11982   InitUserDataDirectory();
11983
11984   if (!(file = fopen(filename, MODE_WRITE)))
11985   {
11986     Warn("cannot write setup file '%s'", filename);
11987
11988     return;
11989   }
11990
11991   fprintFileHeader(file, SETUP_FILENAME);
11992
11993   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11994   {
11995     // just to make things nicer :)
11996     if (global_setup_tokens[i].value == &setup.multiple_users           ||
11997         global_setup_tokens[i].value == &setup.sound                    ||
11998         global_setup_tokens[i].value == &setup.graphics_set             ||
11999         global_setup_tokens[i].value == &setup.volume_simple            ||
12000         global_setup_tokens[i].value == &setup.network_mode             ||
12001         global_setup_tokens[i].value == &setup.touch.control_type       ||
12002         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12003         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12004       fprintf(file, "\n");
12005
12006     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12007   }
12008
12009   for (i = 0; i < 2; i++)
12010   {
12011     int grid_xsize = setup.touch.grid_xsize[i];
12012     int grid_ysize = setup.touch.grid_ysize[i];
12013     int x, y;
12014
12015     fprintf(file, "\n");
12016
12017     for (y = 0; y < grid_ysize; y++)
12018     {
12019       char token_string[MAX_LINE_LEN];
12020       char value_string[MAX_LINE_LEN];
12021
12022       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12023
12024       for (x = 0; x < grid_xsize; x++)
12025       {
12026         char c = setup.touch.grid_button[i][x][y];
12027
12028         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12029       }
12030
12031       value_string[grid_xsize] = '\0';
12032
12033       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12034     }
12035   }
12036
12037   fprintf(file, "\n");
12038   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12039     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12040
12041   fprintf(file, "\n");
12042   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12043     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12044
12045   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12046   {
12047     char prefix[30];
12048
12049     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12050     fprintf(file, "\n");
12051
12052     setup_input = setup.input[pnr];
12053     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12054       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12055   }
12056
12057   fprintf(file, "\n");
12058   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12059     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12060
12061   // (internal setup values not saved to user setup file)
12062
12063   fprintf(file, "\n");
12064   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12065     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12066         setup.debug.xsn_mode != AUTO)
12067       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12068
12069   fprintf(file, "\n");
12070   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12071     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12072
12073   fclose(file);
12074
12075   SetFilePermissions(filename, PERMS_PRIVATE);
12076 }
12077
12078 void SaveSetup_AutoSetup(void)
12079 {
12080   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12081   FILE *file;
12082   int i;
12083
12084   InitUserDataDirectory();
12085
12086   if (!(file = fopen(filename, MODE_WRITE)))
12087   {
12088     Warn("cannot write auto setup file '%s'", filename);
12089
12090     free(filename);
12091
12092     return;
12093   }
12094
12095   fprintFileHeader(file, AUTOSETUP_FILENAME);
12096
12097   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12098     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12099
12100   fclose(file);
12101
12102   SetFilePermissions(filename, PERMS_PRIVATE);
12103
12104   free(filename);
12105 }
12106
12107 void SaveSetup_ServerSetup(void)
12108 {
12109   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12110   FILE *file;
12111   int i;
12112
12113   InitUserDataDirectory();
12114
12115   if (!(file = fopen(filename, MODE_WRITE)))
12116   {
12117     Warn("cannot write server setup file '%s'", filename);
12118
12119     free(filename);
12120
12121     return;
12122   }
12123
12124   fprintFileHeader(file, SERVERSETUP_FILENAME);
12125
12126   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12127   {
12128     // just to make things nicer :)
12129     if (server_setup_tokens[i].value == &setup.use_api_server)
12130       fprintf(file, "\n");
12131
12132     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12133   }
12134
12135   fclose(file);
12136
12137   SetFilePermissions(filename, PERMS_PRIVATE);
12138
12139   free(filename);
12140 }
12141
12142 void SaveSetup_EditorCascade(void)
12143 {
12144   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12145   FILE *file;
12146   int i;
12147
12148   InitUserDataDirectory();
12149
12150   if (!(file = fopen(filename, MODE_WRITE)))
12151   {
12152     Warn("cannot write editor cascade state file '%s'", filename);
12153
12154     free(filename);
12155
12156     return;
12157   }
12158
12159   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12160
12161   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12162     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12163
12164   fclose(file);
12165
12166   SetFilePermissions(filename, PERMS_PRIVATE);
12167
12168   free(filename);
12169 }
12170
12171 void SaveSetup(void)
12172 {
12173   SaveSetup_Default();
12174   SaveSetup_AutoSetup();
12175   SaveSetup_ServerSetup();
12176   SaveSetup_EditorCascade();
12177 }
12178
12179 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12180                                                   char *filename)
12181 {
12182   FILE *file;
12183
12184   if (!(file = fopen(filename, MODE_WRITE)))
12185   {
12186     Warn("cannot write game controller mappings file '%s'", filename);
12187
12188     return;
12189   }
12190
12191   BEGIN_HASH_ITERATION(mappings_hash, itr)
12192   {
12193     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12194   }
12195   END_HASH_ITERATION(mappings_hash, itr)
12196
12197   fclose(file);
12198 }
12199
12200 void SaveSetup_AddGameControllerMapping(char *mapping)
12201 {
12202   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12203   SetupFileHash *mappings_hash = newSetupFileHash();
12204
12205   InitUserDataDirectory();
12206
12207   // load existing personal game controller mappings
12208   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12209
12210   // add new mapping to personal game controller mappings
12211   addGameControllerMappingToHash(mappings_hash, mapping);
12212
12213   // save updated personal game controller mappings
12214   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12215
12216   freeSetupFileHash(mappings_hash);
12217   free(filename);
12218 }
12219
12220 void LoadCustomElementDescriptions(void)
12221 {
12222   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12223   SetupFileHash *setup_file_hash;
12224   int i;
12225
12226   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12227   {
12228     if (element_info[i].custom_description != NULL)
12229     {
12230       free(element_info[i].custom_description);
12231       element_info[i].custom_description = NULL;
12232     }
12233   }
12234
12235   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12236     return;
12237
12238   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12239   {
12240     char *token = getStringCat2(element_info[i].token_name, ".name");
12241     char *value = getHashEntry(setup_file_hash, token);
12242
12243     if (value != NULL)
12244       element_info[i].custom_description = getStringCopy(value);
12245
12246     free(token);
12247   }
12248
12249   freeSetupFileHash(setup_file_hash);
12250 }
12251
12252 static int getElementFromToken(char *token)
12253 {
12254   char *value = getHashEntry(element_token_hash, token);
12255
12256   if (value != NULL)
12257     return atoi(value);
12258
12259   Warn("unknown element token '%s'", token);
12260
12261   return EL_UNDEFINED;
12262 }
12263
12264 void FreeGlobalAnimEventInfo(void)
12265 {
12266   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12267
12268   if (gaei->event_list == NULL)
12269     return;
12270
12271   int i;
12272
12273   for (i = 0; i < gaei->num_event_lists; i++)
12274   {
12275     checked_free(gaei->event_list[i]->event_value);
12276     checked_free(gaei->event_list[i]);
12277   }
12278
12279   checked_free(gaei->event_list);
12280
12281   gaei->event_list = NULL;
12282   gaei->num_event_lists = 0;
12283 }
12284
12285 static int AddGlobalAnimEventList(void)
12286 {
12287   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12288   int list_pos = gaei->num_event_lists++;
12289
12290   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12291                                      sizeof(struct GlobalAnimEventListInfo *));
12292
12293   gaei->event_list[list_pos] =
12294     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12295
12296   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12297
12298   gaeli->event_value = NULL;
12299   gaeli->num_event_values = 0;
12300
12301   return list_pos;
12302 }
12303
12304 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12305 {
12306   // do not add empty global animation events
12307   if (event_value == ANIM_EVENT_NONE)
12308     return list_pos;
12309
12310   // if list position is undefined, create new list
12311   if (list_pos == ANIM_EVENT_UNDEFINED)
12312     list_pos = AddGlobalAnimEventList();
12313
12314   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12315   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12316   int value_pos = gaeli->num_event_values++;
12317
12318   gaeli->event_value = checked_realloc(gaeli->event_value,
12319                                        gaeli->num_event_values * sizeof(int *));
12320
12321   gaeli->event_value[value_pos] = event_value;
12322
12323   return list_pos;
12324 }
12325
12326 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12327 {
12328   if (list_pos == ANIM_EVENT_UNDEFINED)
12329     return ANIM_EVENT_NONE;
12330
12331   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12332   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12333
12334   return gaeli->event_value[value_pos];
12335 }
12336
12337 int GetGlobalAnimEventValueCount(int list_pos)
12338 {
12339   if (list_pos == ANIM_EVENT_UNDEFINED)
12340     return 0;
12341
12342   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12343   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12344
12345   return gaeli->num_event_values;
12346 }
12347
12348 // This function checks if a string <s> of the format "string1, string2, ..."
12349 // exactly contains a string <s_contained>.
12350
12351 static boolean string_has_parameter(char *s, char *s_contained)
12352 {
12353   char *substring;
12354
12355   if (s == NULL || s_contained == NULL)
12356     return FALSE;
12357
12358   if (strlen(s_contained) > strlen(s))
12359     return FALSE;
12360
12361   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12362   {
12363     char next_char = s[strlen(s_contained)];
12364
12365     // check if next character is delimiter or whitespace
12366     if (next_char == ',' || next_char == '\0' ||
12367         next_char == ' ' || next_char == '\t')
12368       return TRUE;
12369   }
12370
12371   // check if string contains another parameter string after a comma
12372   substring = strchr(s, ',');
12373   if (substring == NULL)        // string does not contain a comma
12374     return FALSE;
12375
12376   // advance string pointer to next character after the comma
12377   substring++;
12378
12379   // skip potential whitespaces after the comma
12380   while (*substring == ' ' || *substring == '\t')
12381     substring++;
12382
12383   return string_has_parameter(substring, s_contained);
12384 }
12385
12386 static int get_anim_parameter_value_ce(char *s)
12387 {
12388   char *s_ptr = s;
12389   char *pattern_1 = "ce_change:custom_";
12390   char *pattern_2 = ".page_";
12391   int pattern_1_len = strlen(pattern_1);
12392   char *matching_char = strstr(s_ptr, pattern_1);
12393   int result = ANIM_EVENT_NONE;
12394
12395   if (matching_char == NULL)
12396     return ANIM_EVENT_NONE;
12397
12398   result = ANIM_EVENT_CE_CHANGE;
12399
12400   s_ptr = matching_char + pattern_1_len;
12401
12402   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12403   if (*s_ptr >= '0' && *s_ptr <= '9')
12404   {
12405     int gic_ce_nr = (*s_ptr++ - '0');
12406
12407     if (*s_ptr >= '0' && *s_ptr <= '9')
12408     {
12409       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12410
12411       if (*s_ptr >= '0' && *s_ptr <= '9')
12412         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12413     }
12414
12415     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12416       return ANIM_EVENT_NONE;
12417
12418     // custom element stored as 0 to 255
12419     gic_ce_nr--;
12420
12421     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12422   }
12423   else
12424   {
12425     // invalid custom element number specified
12426
12427     return ANIM_EVENT_NONE;
12428   }
12429
12430   // check for change page number ("page_X" or "page_XX") (optional)
12431   if (strPrefix(s_ptr, pattern_2))
12432   {
12433     s_ptr += strlen(pattern_2);
12434
12435     if (*s_ptr >= '0' && *s_ptr <= '9')
12436     {
12437       int gic_page_nr = (*s_ptr++ - '0');
12438
12439       if (*s_ptr >= '0' && *s_ptr <= '9')
12440         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12441
12442       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12443         return ANIM_EVENT_NONE;
12444
12445       // change page stored as 1 to 32 (0 means "all change pages")
12446
12447       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12448     }
12449     else
12450     {
12451       // invalid animation part number specified
12452
12453       return ANIM_EVENT_NONE;
12454     }
12455   }
12456
12457   // discard result if next character is neither delimiter nor whitespace
12458   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12459         *s_ptr == ' ' || *s_ptr == '\t'))
12460     return ANIM_EVENT_NONE;
12461
12462   return result;
12463 }
12464
12465 static int get_anim_parameter_value(char *s)
12466 {
12467   int event_value[] =
12468   {
12469     ANIM_EVENT_CLICK,
12470     ANIM_EVENT_INIT,
12471     ANIM_EVENT_START,
12472     ANIM_EVENT_END,
12473     ANIM_EVENT_POST
12474   };
12475   char *pattern_1[] =
12476   {
12477     "click:anim_",
12478     "init:anim_",
12479     "start:anim_",
12480     "end:anim_",
12481     "post:anim_"
12482   };
12483   char *pattern_2 = ".part_";
12484   char *matching_char = NULL;
12485   char *s_ptr = s;
12486   int pattern_1_len = 0;
12487   int result = ANIM_EVENT_NONE;
12488   int i;
12489
12490   result = get_anim_parameter_value_ce(s);
12491
12492   if (result != ANIM_EVENT_NONE)
12493     return result;
12494
12495   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12496   {
12497     matching_char = strstr(s_ptr, pattern_1[i]);
12498     pattern_1_len = strlen(pattern_1[i]);
12499     result = event_value[i];
12500
12501     if (matching_char != NULL)
12502       break;
12503   }
12504
12505   if (matching_char == NULL)
12506     return ANIM_EVENT_NONE;
12507
12508   s_ptr = matching_char + pattern_1_len;
12509
12510   // check for main animation number ("anim_X" or "anim_XX")
12511   if (*s_ptr >= '0' && *s_ptr <= '9')
12512   {
12513     int gic_anim_nr = (*s_ptr++ - '0');
12514
12515     if (*s_ptr >= '0' && *s_ptr <= '9')
12516       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12517
12518     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12519       return ANIM_EVENT_NONE;
12520
12521     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12522   }
12523   else
12524   {
12525     // invalid main animation number specified
12526
12527     return ANIM_EVENT_NONE;
12528   }
12529
12530   // check for animation part number ("part_X" or "part_XX") (optional)
12531   if (strPrefix(s_ptr, pattern_2))
12532   {
12533     s_ptr += strlen(pattern_2);
12534
12535     if (*s_ptr >= '0' && *s_ptr <= '9')
12536     {
12537       int gic_part_nr = (*s_ptr++ - '0');
12538
12539       if (*s_ptr >= '0' && *s_ptr <= '9')
12540         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12541
12542       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12543         return ANIM_EVENT_NONE;
12544
12545       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12546     }
12547     else
12548     {
12549       // invalid animation part number specified
12550
12551       return ANIM_EVENT_NONE;
12552     }
12553   }
12554
12555   // discard result if next character is neither delimiter nor whitespace
12556   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12557         *s_ptr == ' ' || *s_ptr == '\t'))
12558     return ANIM_EVENT_NONE;
12559
12560   return result;
12561 }
12562
12563 static int get_anim_parameter_values(char *s)
12564 {
12565   int list_pos = ANIM_EVENT_UNDEFINED;
12566   int event_value = ANIM_EVENT_DEFAULT;
12567
12568   if (string_has_parameter(s, "any"))
12569     event_value |= ANIM_EVENT_ANY;
12570
12571   if (string_has_parameter(s, "click:self") ||
12572       string_has_parameter(s, "click") ||
12573       string_has_parameter(s, "self"))
12574     event_value |= ANIM_EVENT_SELF;
12575
12576   if (string_has_parameter(s, "unclick:any"))
12577     event_value |= ANIM_EVENT_UNCLICK_ANY;
12578
12579   // if animation event found, add it to global animation event list
12580   if (event_value != ANIM_EVENT_NONE)
12581     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12582
12583   while (s != NULL)
12584   {
12585     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12586     event_value = get_anim_parameter_value(s);
12587
12588     // if animation event found, add it to global animation event list
12589     if (event_value != ANIM_EVENT_NONE)
12590       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12591
12592     // continue with next part of the string, starting with next comma
12593     s = strchr(s + 1, ',');
12594   }
12595
12596   return list_pos;
12597 }
12598
12599 static int get_anim_action_parameter_value(char *token)
12600 {
12601   // check most common default case first to massively speed things up
12602   if (strEqual(token, ARG_UNDEFINED))
12603     return ANIM_EVENT_ACTION_NONE;
12604
12605   int result = getImageIDFromToken(token);
12606
12607   if (result == -1)
12608   {
12609     char *gfx_token = getStringCat2("gfx.", token);
12610
12611     result = getImageIDFromToken(gfx_token);
12612
12613     checked_free(gfx_token);
12614   }
12615
12616   if (result == -1)
12617   {
12618     Key key = getKeyFromX11KeyName(token);
12619
12620     if (key != KSYM_UNDEFINED)
12621       result = -(int)key;
12622   }
12623
12624   if (result == -1)
12625   {
12626     if (isURL(token))
12627     {
12628       result = get_hash_from_string(token);     // unsigned int => int
12629       result = ABS(result);                     // may be negative now
12630       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12631
12632       setHashEntry(anim_url_hash, int2str(result, 0), token);
12633     }
12634   }
12635
12636   if (result == -1)
12637     result = ANIM_EVENT_ACTION_NONE;
12638
12639   return result;
12640 }
12641
12642 int get_parameter_value(char *value_raw, char *suffix, int type)
12643 {
12644   char *value = getStringToLower(value_raw);
12645   int result = 0;       // probably a save default value
12646
12647   if (strEqual(suffix, ".direction"))
12648   {
12649     result = (strEqual(value, "left")  ? MV_LEFT :
12650               strEqual(value, "right") ? MV_RIGHT :
12651               strEqual(value, "up")    ? MV_UP :
12652               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12653   }
12654   else if (strEqual(suffix, ".position"))
12655   {
12656     result = (strEqual(value, "left")   ? POS_LEFT :
12657               strEqual(value, "right")  ? POS_RIGHT :
12658               strEqual(value, "top")    ? POS_TOP :
12659               strEqual(value, "upper")  ? POS_UPPER :
12660               strEqual(value, "middle") ? POS_MIDDLE :
12661               strEqual(value, "lower")  ? POS_LOWER :
12662               strEqual(value, "bottom") ? POS_BOTTOM :
12663               strEqual(value, "any")    ? POS_ANY :
12664               strEqual(value, "ce")     ? POS_CE :
12665               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12666               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12667   }
12668   else if (strEqual(suffix, ".align"))
12669   {
12670     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12671               strEqual(value, "right")  ? ALIGN_RIGHT :
12672               strEqual(value, "center") ? ALIGN_CENTER :
12673               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12674   }
12675   else if (strEqual(suffix, ".valign"))
12676   {
12677     result = (strEqual(value, "top")    ? VALIGN_TOP :
12678               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12679               strEqual(value, "middle") ? VALIGN_MIDDLE :
12680               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12681   }
12682   else if (strEqual(suffix, ".anim_mode"))
12683   {
12684     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12685               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12686               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12687               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12688               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12689               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12690               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12691               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12692               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12693               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12694               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12695               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12696               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12697               string_has_parameter(value, "all")        ? ANIM_ALL :
12698               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12699               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12700               ANIM_DEFAULT);
12701
12702     if (string_has_parameter(value, "once"))
12703       result |= ANIM_ONCE;
12704
12705     if (string_has_parameter(value, "reverse"))
12706       result |= ANIM_REVERSE;
12707
12708     if (string_has_parameter(value, "opaque_player"))
12709       result |= ANIM_OPAQUE_PLAYER;
12710
12711     if (string_has_parameter(value, "static_panel"))
12712       result |= ANIM_STATIC_PANEL;
12713   }
12714   else if (strEqual(suffix, ".init_event") ||
12715            strEqual(suffix, ".anim_event"))
12716   {
12717     result = get_anim_parameter_values(value);
12718   }
12719   else if (strEqual(suffix, ".init_delay_action") ||
12720            strEqual(suffix, ".anim_delay_action") ||
12721            strEqual(suffix, ".post_delay_action") ||
12722            strEqual(suffix, ".init_event_action") ||
12723            strEqual(suffix, ".anim_event_action"))
12724   {
12725     result = get_anim_action_parameter_value(value_raw);
12726   }
12727   else if (strEqual(suffix, ".class"))
12728   {
12729     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12730               get_hash_from_string(value));
12731   }
12732   else if (strEqual(suffix, ".style"))
12733   {
12734     result = STYLE_DEFAULT;
12735
12736     if (string_has_parameter(value, "accurate_borders"))
12737       result |= STYLE_ACCURATE_BORDERS;
12738
12739     if (string_has_parameter(value, "inner_corners"))
12740       result |= STYLE_INNER_CORNERS;
12741
12742     if (string_has_parameter(value, "reverse"))
12743       result |= STYLE_REVERSE;
12744
12745     if (string_has_parameter(value, "leftmost_position"))
12746       result |= STYLE_LEFTMOST_POSITION;
12747
12748     if (string_has_parameter(value, "block_clicks"))
12749       result |= STYLE_BLOCK;
12750
12751     if (string_has_parameter(value, "passthrough_clicks"))
12752       result |= STYLE_PASSTHROUGH;
12753
12754     if (string_has_parameter(value, "multiple_actions"))
12755       result |= STYLE_MULTIPLE_ACTIONS;
12756
12757     if (string_has_parameter(value, "consume_ce_event"))
12758       result |= STYLE_CONSUME_CE_EVENT;
12759   }
12760   else if (strEqual(suffix, ".fade_mode"))
12761   {
12762     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12763               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12764               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12765               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12766               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12767               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12768               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12769               FADE_MODE_DEFAULT);
12770   }
12771   else if (strEqual(suffix, ".auto_delay_unit"))
12772   {
12773     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12774               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12775               AUTO_DELAY_UNIT_DEFAULT);
12776   }
12777   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12778   {
12779     result = gfx.get_font_from_token_function(value);
12780   }
12781   else          // generic parameter of type integer or boolean
12782   {
12783     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12784               type == TYPE_INTEGER ? get_integer_from_string(value) :
12785               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12786               ARG_UNDEFINED_VALUE);
12787   }
12788
12789   free(value);
12790
12791   return result;
12792 }
12793
12794 static int get_token_parameter_value(char *token, char *value_raw)
12795 {
12796   char *suffix;
12797
12798   if (token == NULL || value_raw == NULL)
12799     return ARG_UNDEFINED_VALUE;
12800
12801   suffix = strrchr(token, '.');
12802   if (suffix == NULL)
12803     suffix = token;
12804
12805   if (strEqual(suffix, ".element"))
12806     return getElementFromToken(value_raw);
12807
12808   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12809   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12810 }
12811
12812 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12813                                      boolean ignore_defaults)
12814 {
12815   int i;
12816
12817   for (i = 0; image_config_vars[i].token != NULL; i++)
12818   {
12819     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12820
12821     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12822     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12823       continue;
12824
12825     if (value != NULL)
12826       *image_config_vars[i].value =
12827         get_token_parameter_value(image_config_vars[i].token, value);
12828   }
12829 }
12830
12831 void InitMenuDesignSettings_Static(void)
12832 {
12833   // always start with reliable default values from static default config
12834   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12835 }
12836
12837 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12838 {
12839   int i;
12840
12841   // the following initializes hierarchical values from static configuration
12842
12843   // special case: initialize "ARG_DEFAULT" values in static default config
12844   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12845   titlescreen_initial_first_default.fade_mode  =
12846     title_initial_first_default.fade_mode;
12847   titlescreen_initial_first_default.fade_delay =
12848     title_initial_first_default.fade_delay;
12849   titlescreen_initial_first_default.post_delay =
12850     title_initial_first_default.post_delay;
12851   titlescreen_initial_first_default.auto_delay =
12852     title_initial_first_default.auto_delay;
12853   titlescreen_initial_first_default.auto_delay_unit =
12854     title_initial_first_default.auto_delay_unit;
12855   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12856   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12857   titlescreen_first_default.post_delay = title_first_default.post_delay;
12858   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12859   titlescreen_first_default.auto_delay_unit =
12860     title_first_default.auto_delay_unit;
12861   titlemessage_initial_first_default.fade_mode  =
12862     title_initial_first_default.fade_mode;
12863   titlemessage_initial_first_default.fade_delay =
12864     title_initial_first_default.fade_delay;
12865   titlemessage_initial_first_default.post_delay =
12866     title_initial_first_default.post_delay;
12867   titlemessage_initial_first_default.auto_delay =
12868     title_initial_first_default.auto_delay;
12869   titlemessage_initial_first_default.auto_delay_unit =
12870     title_initial_first_default.auto_delay_unit;
12871   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12872   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12873   titlemessage_first_default.post_delay = title_first_default.post_delay;
12874   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12875   titlemessage_first_default.auto_delay_unit =
12876     title_first_default.auto_delay_unit;
12877
12878   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12879   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12880   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12881   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12882   titlescreen_initial_default.auto_delay_unit =
12883     title_initial_default.auto_delay_unit;
12884   titlescreen_default.fade_mode  = title_default.fade_mode;
12885   titlescreen_default.fade_delay = title_default.fade_delay;
12886   titlescreen_default.post_delay = title_default.post_delay;
12887   titlescreen_default.auto_delay = title_default.auto_delay;
12888   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12889   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12890   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12891   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12892   titlemessage_initial_default.auto_delay_unit =
12893     title_initial_default.auto_delay_unit;
12894   titlemessage_default.fade_mode  = title_default.fade_mode;
12895   titlemessage_default.fade_delay = title_default.fade_delay;
12896   titlemessage_default.post_delay = title_default.post_delay;
12897   titlemessage_default.auto_delay = title_default.auto_delay;
12898   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12899
12900   // special case: initialize "ARG_DEFAULT" values in static default config
12901   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12902   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12903   {
12904     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12905     titlescreen_first[i] = titlescreen_first_default;
12906     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12907     titlemessage_first[i] = titlemessage_first_default;
12908
12909     titlescreen_initial[i] = titlescreen_initial_default;
12910     titlescreen[i] = titlescreen_default;
12911     titlemessage_initial[i] = titlemessage_initial_default;
12912     titlemessage[i] = titlemessage_default;
12913   }
12914
12915   // special case: initialize "ARG_DEFAULT" values in static default config
12916   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12917   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12918   {
12919     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12920       continue;
12921
12922     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12923     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12924     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12925   }
12926
12927   // special case: initialize "ARG_DEFAULT" values in static default config
12928   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12929   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12930   {
12931     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12932     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12933     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12934
12935     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12936       continue;
12937
12938     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12939   }
12940 }
12941
12942 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12943 {
12944   static struct
12945   {
12946     struct XY *dst, *src;
12947   }
12948   game_buttons_xy[] =
12949   {
12950     { &game.button.save,        &game.button.stop       },
12951     { &game.button.pause2,      &game.button.pause      },
12952     { &game.button.load,        &game.button.play       },
12953     { &game.button.undo,        &game.button.stop       },
12954     { &game.button.redo,        &game.button.play       },
12955
12956     { NULL,                     NULL                    }
12957   };
12958   int i, j;
12959
12960   // special case: initialize later added SETUP list size from LEVELS value
12961   if (menu.list_size[GAME_MODE_SETUP] == -1)
12962     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
12963
12964   // set default position for snapshot buttons to stop/pause/play buttons
12965   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
12966     if ((*game_buttons_xy[i].dst).x == -1 &&
12967         (*game_buttons_xy[i].dst).y == -1)
12968       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
12969
12970   // --------------------------------------------------------------------------
12971   // dynamic viewports (including playfield margins, borders and alignments)
12972   // --------------------------------------------------------------------------
12973
12974   // dynamic viewports currently only supported for landscape mode
12975   int display_width  = MAX(video.display_width, video.display_height);
12976   int display_height = MIN(video.display_width, video.display_height);
12977
12978   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12979   {
12980     struct RectWithBorder *vp_window    = &viewport.window[i];
12981     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
12982     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
12983     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
12984     boolean dynamic_window_width     = (vp_window->min_width     != -1);
12985     boolean dynamic_window_height    = (vp_window->min_height    != -1);
12986     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
12987     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
12988
12989     // adjust window size if min/max width/height is specified
12990
12991     if (vp_window->min_width != -1)
12992     {
12993       int window_width = display_width;
12994
12995       // when using static window height, use aspect ratio of display
12996       if (vp_window->min_height == -1)
12997         window_width = vp_window->height * display_width / display_height;
12998
12999       vp_window->width = MAX(vp_window->min_width, window_width);
13000     }
13001
13002     if (vp_window->min_height != -1)
13003     {
13004       int window_height = display_height;
13005
13006       // when using static window width, use aspect ratio of display
13007       if (vp_window->min_width == -1)
13008         window_height = vp_window->width * display_height / display_width;
13009
13010       vp_window->height = MAX(vp_window->min_height, window_height);
13011     }
13012
13013     if (vp_window->max_width != -1)
13014       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13015
13016     if (vp_window->max_height != -1)
13017       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13018
13019     int playfield_width  = vp_window->width;
13020     int playfield_height = vp_window->height;
13021
13022     // adjust playfield size and position according to specified margins
13023
13024     playfield_width  -= vp_playfield->margin_left;
13025     playfield_width  -= vp_playfield->margin_right;
13026
13027     playfield_height -= vp_playfield->margin_top;
13028     playfield_height -= vp_playfield->margin_bottom;
13029
13030     // adjust playfield size if min/max width/height is specified
13031
13032     if (vp_playfield->min_width != -1)
13033       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13034
13035     if (vp_playfield->min_height != -1)
13036       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13037
13038     if (vp_playfield->max_width != -1)
13039       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13040
13041     if (vp_playfield->max_height != -1)
13042       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13043
13044     // adjust playfield position according to specified alignment
13045
13046     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13047       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13048     else if (vp_playfield->align == ALIGN_CENTER)
13049       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13050     else if (vp_playfield->align == ALIGN_RIGHT)
13051       vp_playfield->x += playfield_width - vp_playfield->width;
13052
13053     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13054       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13055     else if (vp_playfield->valign == VALIGN_MIDDLE)
13056       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13057     else if (vp_playfield->valign == VALIGN_BOTTOM)
13058       vp_playfield->y += playfield_height - vp_playfield->height;
13059
13060     vp_playfield->x += vp_playfield->margin_left;
13061     vp_playfield->y += vp_playfield->margin_top;
13062
13063     // adjust individual playfield borders if only default border is specified
13064
13065     if (vp_playfield->border_left == -1)
13066       vp_playfield->border_left = vp_playfield->border_size;
13067     if (vp_playfield->border_right == -1)
13068       vp_playfield->border_right = vp_playfield->border_size;
13069     if (vp_playfield->border_top == -1)
13070       vp_playfield->border_top = vp_playfield->border_size;
13071     if (vp_playfield->border_bottom == -1)
13072       vp_playfield->border_bottom = vp_playfield->border_size;
13073
13074     // set dynamic playfield borders if borders are specified as undefined
13075     // (but only if window size was dynamic and playfield size was static)
13076
13077     if (dynamic_window_width && !dynamic_playfield_width)
13078     {
13079       if (vp_playfield->border_left == -1)
13080       {
13081         vp_playfield->border_left = (vp_playfield->x -
13082                                      vp_playfield->margin_left);
13083         vp_playfield->x     -= vp_playfield->border_left;
13084         vp_playfield->width += vp_playfield->border_left;
13085       }
13086
13087       if (vp_playfield->border_right == -1)
13088       {
13089         vp_playfield->border_right = (vp_window->width -
13090                                       vp_playfield->x -
13091                                       vp_playfield->width -
13092                                       vp_playfield->margin_right);
13093         vp_playfield->width += vp_playfield->border_right;
13094       }
13095     }
13096
13097     if (dynamic_window_height && !dynamic_playfield_height)
13098     {
13099       if (vp_playfield->border_top == -1)
13100       {
13101         vp_playfield->border_top = (vp_playfield->y -
13102                                     vp_playfield->margin_top);
13103         vp_playfield->y      -= vp_playfield->border_top;
13104         vp_playfield->height += vp_playfield->border_top;
13105       }
13106
13107       if (vp_playfield->border_bottom == -1)
13108       {
13109         vp_playfield->border_bottom = (vp_window->height -
13110                                        vp_playfield->y -
13111                                        vp_playfield->height -
13112                                        vp_playfield->margin_bottom);
13113         vp_playfield->height += vp_playfield->border_bottom;
13114       }
13115     }
13116
13117     // adjust playfield size to be a multiple of a defined alignment tile size
13118
13119     int align_size = vp_playfield->align_size;
13120     int playfield_xtiles = vp_playfield->width  / align_size;
13121     int playfield_ytiles = vp_playfield->height / align_size;
13122     int playfield_width_corrected  = playfield_xtiles * align_size;
13123     int playfield_height_corrected = playfield_ytiles * align_size;
13124     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13125                                  i == GFX_SPECIAL_ARG_EDITOR);
13126
13127     if (is_playfield_mode &&
13128         dynamic_playfield_width &&
13129         vp_playfield->width != playfield_width_corrected)
13130     {
13131       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13132
13133       vp_playfield->width = playfield_width_corrected;
13134
13135       if (vp_playfield->align == ALIGN_LEFT)
13136       {
13137         vp_playfield->border_left += playfield_xdiff;
13138       }
13139       else if (vp_playfield->align == ALIGN_RIGHT)
13140       {
13141         vp_playfield->border_right += playfield_xdiff;
13142       }
13143       else if (vp_playfield->align == ALIGN_CENTER)
13144       {
13145         int border_left_diff  = playfield_xdiff / 2;
13146         int border_right_diff = playfield_xdiff - border_left_diff;
13147
13148         vp_playfield->border_left  += border_left_diff;
13149         vp_playfield->border_right += border_right_diff;
13150       }
13151     }
13152
13153     if (is_playfield_mode &&
13154         dynamic_playfield_height &&
13155         vp_playfield->height != playfield_height_corrected)
13156     {
13157       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13158
13159       vp_playfield->height = playfield_height_corrected;
13160
13161       if (vp_playfield->valign == VALIGN_TOP)
13162       {
13163         vp_playfield->border_top += playfield_ydiff;
13164       }
13165       else if (vp_playfield->align == VALIGN_BOTTOM)
13166       {
13167         vp_playfield->border_right += playfield_ydiff;
13168       }
13169       else if (vp_playfield->align == VALIGN_MIDDLE)
13170       {
13171         int border_top_diff    = playfield_ydiff / 2;
13172         int border_bottom_diff = playfield_ydiff - border_top_diff;
13173
13174         vp_playfield->border_top    += border_top_diff;
13175         vp_playfield->border_bottom += border_bottom_diff;
13176       }
13177     }
13178
13179     // adjust door positions according to specified alignment
13180
13181     for (j = 0; j < 2; j++)
13182     {
13183       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13184
13185       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13186         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13187       else if (vp_door->align == ALIGN_CENTER)
13188         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13189       else if (vp_door->align == ALIGN_RIGHT)
13190         vp_door->x += vp_window->width - vp_door->width;
13191
13192       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13193         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13194       else if (vp_door->valign == VALIGN_MIDDLE)
13195         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13196       else if (vp_door->valign == VALIGN_BOTTOM)
13197         vp_door->y += vp_window->height - vp_door->height;
13198     }
13199   }
13200 }
13201
13202 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13203 {
13204   static struct
13205   {
13206     struct XYTileSize *dst, *src;
13207     int graphic;
13208   }
13209   editor_buttons_xy[] =
13210   {
13211     {
13212       &editor.button.element_left,      &editor.palette.element_left,
13213       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13214     },
13215     {
13216       &editor.button.element_middle,    &editor.palette.element_middle,
13217       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13218     },
13219     {
13220       &editor.button.element_right,     &editor.palette.element_right,
13221       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13222     },
13223
13224     { NULL,                     NULL                    }
13225   };
13226   int i;
13227
13228   // set default position for element buttons to element graphics
13229   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13230   {
13231     if ((*editor_buttons_xy[i].dst).x == -1 &&
13232         (*editor_buttons_xy[i].dst).y == -1)
13233     {
13234       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13235
13236       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13237
13238       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13239     }
13240   }
13241
13242   // adjust editor palette rows and columns if specified to be dynamic
13243
13244   if (editor.palette.cols == -1)
13245   {
13246     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13247     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13248     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13249
13250     editor.palette.cols = (vp_width - sc_width) / bt_width;
13251
13252     if (editor.palette.x == -1)
13253     {
13254       int palette_width = editor.palette.cols * bt_width + sc_width;
13255
13256       editor.palette.x = (vp_width - palette_width) / 2;
13257     }
13258   }
13259
13260   if (editor.palette.rows == -1)
13261   {
13262     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13263     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13264     int tx_height = getFontHeight(FONT_TEXT_2);
13265
13266     editor.palette.rows = (vp_height - tx_height) / bt_height;
13267
13268     if (editor.palette.y == -1)
13269     {
13270       int palette_height = editor.palette.rows * bt_height + tx_height;
13271
13272       editor.palette.y = (vp_height - palette_height) / 2;
13273     }
13274   }
13275 }
13276
13277 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13278                                                       boolean initialize)
13279 {
13280   // special case: check if network and preview player positions are redefined,
13281   // to compare this later against the main menu level preview being redefined
13282   struct TokenIntPtrInfo menu_config_players[] =
13283   {
13284     { "main.network_players.x", &menu.main.network_players.redefined    },
13285     { "main.network_players.y", &menu.main.network_players.redefined    },
13286     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13287     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13288     { "preview.x",              &preview.redefined                      },
13289     { "preview.y",              &preview.redefined                      }
13290   };
13291   int i;
13292
13293   if (initialize)
13294   {
13295     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13296       *menu_config_players[i].value = FALSE;
13297   }
13298   else
13299   {
13300     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13301       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13302         *menu_config_players[i].value = TRUE;
13303   }
13304 }
13305
13306 static void InitMenuDesignSettings_PreviewPlayers(void)
13307 {
13308   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13309 }
13310
13311 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13312 {
13313   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13314 }
13315
13316 static void LoadMenuDesignSettingsFromFilename(char *filename)
13317 {
13318   static struct TitleFadingInfo tfi;
13319   static struct TitleMessageInfo tmi;
13320   static struct TokenInfo title_tokens[] =
13321   {
13322     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13323     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13324     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13325     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13326     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13327
13328     { -1,               NULL,                   NULL                    }
13329   };
13330   static struct TokenInfo titlemessage_tokens[] =
13331   {
13332     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13333     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13334     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13335     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13336     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13337     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13338     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13339     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13340     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13341     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13342     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13343     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13344     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13345     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13346     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13347     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13348     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13349     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13350
13351     { -1,               NULL,                   NULL                    }
13352   };
13353   static struct
13354   {
13355     struct TitleFadingInfo *info;
13356     char *text;
13357   }
13358   title_info[] =
13359   {
13360     // initialize first titles from "enter screen" definitions, if defined
13361     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13362     { &title_first_default,             "menu.enter_screen.TITLE"       },
13363
13364     // initialize title screens from "next screen" definitions, if defined
13365     { &title_initial_default,           "menu.next_screen.TITLE"        },
13366     { &title_default,                   "menu.next_screen.TITLE"        },
13367
13368     { NULL,                             NULL                            }
13369   };
13370   static struct
13371   {
13372     struct TitleMessageInfo *array;
13373     char *text;
13374   }
13375   titlemessage_arrays[] =
13376   {
13377     // initialize first titles from "enter screen" definitions, if defined
13378     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13379     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13380     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13381     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13382
13383     // initialize titles from "next screen" definitions, if defined
13384     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13385     { titlescreen,                      "menu.next_screen.TITLE"        },
13386     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13387     { titlemessage,                     "menu.next_screen.TITLE"        },
13388
13389     // overwrite titles with title definitions, if defined
13390     { titlescreen_initial_first,        "[title_initial]"               },
13391     { titlescreen_first,                "[title]"                       },
13392     { titlemessage_initial_first,       "[title_initial]"               },
13393     { titlemessage_first,               "[title]"                       },
13394
13395     { titlescreen_initial,              "[title_initial]"               },
13396     { titlescreen,                      "[title]"                       },
13397     { titlemessage_initial,             "[title_initial]"               },
13398     { titlemessage,                     "[title]"                       },
13399
13400     // overwrite titles with title screen/message definitions, if defined
13401     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13402     { titlescreen_first,                "[titlescreen]"                 },
13403     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13404     { titlemessage_first,               "[titlemessage]"                },
13405
13406     { titlescreen_initial,              "[titlescreen_initial]"         },
13407     { titlescreen,                      "[titlescreen]"                 },
13408     { titlemessage_initial,             "[titlemessage_initial]"        },
13409     { titlemessage,                     "[titlemessage]"                },
13410
13411     { NULL,                             NULL                            }
13412   };
13413   SetupFileHash *setup_file_hash;
13414   int i, j, k;
13415
13416   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13417     return;
13418
13419   // the following initializes hierarchical values from dynamic configuration
13420
13421   // special case: initialize with default values that may be overwritten
13422   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13423   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13424   {
13425     struct TokenIntPtrInfo menu_config[] =
13426     {
13427       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13428       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13429       { "menu.list_size",       &menu.list_size[i]      }
13430     };
13431
13432     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13433     {
13434       char *token = menu_config[j].token;
13435       char *value = getHashEntry(setup_file_hash, token);
13436
13437       if (value != NULL)
13438         *menu_config[j].value = get_integer_from_string(value);
13439     }
13440   }
13441
13442   // special case: initialize with default values that may be overwritten
13443   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13444   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13445   {
13446     struct TokenIntPtrInfo menu_config[] =
13447     {
13448       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13449       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13450       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13451       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13452       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13453     };
13454
13455     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13456     {
13457       char *token = menu_config[j].token;
13458       char *value = getHashEntry(setup_file_hash, token);
13459
13460       if (value != NULL)
13461         *menu_config[j].value = get_integer_from_string(value);
13462     }
13463   }
13464
13465   // special case: initialize with default values that may be overwritten
13466   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13467   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13468   {
13469     struct TokenIntPtrInfo menu_config[] =
13470     {
13471       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13472       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13473     };
13474
13475     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13476     {
13477       char *token = menu_config[j].token;
13478       char *value = getHashEntry(setup_file_hash, token);
13479
13480       if (value != NULL)
13481         *menu_config[j].value = get_integer_from_string(value);
13482     }
13483   }
13484
13485   // special case: initialize with default values that may be overwritten
13486   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13487   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13488   {
13489     struct TokenIntPtrInfo menu_config[] =
13490     {
13491       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13492       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13493       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13494       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13495       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13496       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13497       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13498       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13499       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13500       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13501     };
13502
13503     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13504     {
13505       char *token = menu_config[j].token;
13506       char *value = getHashEntry(setup_file_hash, token);
13507
13508       if (value != NULL)
13509         *menu_config[j].value = get_integer_from_string(value);
13510     }
13511   }
13512
13513   // special case: initialize with default values that may be overwritten
13514   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13515   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13516   {
13517     struct TokenIntPtrInfo menu_config[] =
13518     {
13519       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13520       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13521       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13522       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13523       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13524       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13525       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13526       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13527       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13528     };
13529
13530     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13531     {
13532       char *token = menu_config[j].token;
13533       char *value = getHashEntry(setup_file_hash, token);
13534
13535       if (value != NULL)
13536         *menu_config[j].value = get_token_parameter_value(token, value);
13537     }
13538   }
13539
13540   // special case: initialize with default values that may be overwritten
13541   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13542   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13543   {
13544     struct
13545     {
13546       char *token_prefix;
13547       struct RectWithBorder *struct_ptr;
13548     }
13549     vp_struct[] =
13550     {
13551       { "viewport.window",      &viewport.window[i]     },
13552       { "viewport.playfield",   &viewport.playfield[i]  },
13553       { "viewport.door_1",      &viewport.door_1[i]     },
13554       { "viewport.door_2",      &viewport.door_2[i]     }
13555     };
13556
13557     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13558     {
13559       struct TokenIntPtrInfo vp_config[] =
13560       {
13561         { ".x",                 &vp_struct[j].struct_ptr->x             },
13562         { ".y",                 &vp_struct[j].struct_ptr->y             },
13563         { ".width",             &vp_struct[j].struct_ptr->width         },
13564         { ".height",            &vp_struct[j].struct_ptr->height        },
13565         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13566         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13567         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13568         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13569         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13570         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13571         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13572         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13573         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13574         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13575         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13576         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13577         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13578         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13579         { ".align",             &vp_struct[j].struct_ptr->align         },
13580         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13581       };
13582
13583       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13584       {
13585         char *token = getStringCat2(vp_struct[j].token_prefix,
13586                                     vp_config[k].token);
13587         char *value = getHashEntry(setup_file_hash, token);
13588
13589         if (value != NULL)
13590           *vp_config[k].value = get_token_parameter_value(token, value);
13591
13592         free(token);
13593       }
13594     }
13595   }
13596
13597   // special case: initialize with default values that may be overwritten
13598   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13599   for (i = 0; title_info[i].info != NULL; i++)
13600   {
13601     struct TitleFadingInfo *info = title_info[i].info;
13602     char *base_token = title_info[i].text;
13603
13604     for (j = 0; title_tokens[j].type != -1; j++)
13605     {
13606       char *token = getStringCat2(base_token, title_tokens[j].text);
13607       char *value = getHashEntry(setup_file_hash, token);
13608
13609       if (value != NULL)
13610       {
13611         int parameter_value = get_token_parameter_value(token, value);
13612
13613         tfi = *info;
13614
13615         *(int *)title_tokens[j].value = (int)parameter_value;
13616
13617         *info = tfi;
13618       }
13619
13620       free(token);
13621     }
13622   }
13623
13624   // special case: initialize with default values that may be overwritten
13625   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13626   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13627   {
13628     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13629     char *base_token = titlemessage_arrays[i].text;
13630
13631     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13632     {
13633       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13634       char *value = getHashEntry(setup_file_hash, token);
13635
13636       if (value != NULL)
13637       {
13638         int parameter_value = get_token_parameter_value(token, value);
13639
13640         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13641         {
13642           tmi = array[k];
13643
13644           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13645             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13646           else
13647             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13648
13649           array[k] = tmi;
13650         }
13651       }
13652
13653       free(token);
13654     }
13655   }
13656
13657   // read (and overwrite with) values that may be specified in config file
13658   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13659
13660   // special case: check if network and preview player positions are redefined
13661   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13662
13663   freeSetupFileHash(setup_file_hash);
13664 }
13665
13666 void LoadMenuDesignSettings(void)
13667 {
13668   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13669
13670   InitMenuDesignSettings_Static();
13671   InitMenuDesignSettings_SpecialPreProcessing();
13672   InitMenuDesignSettings_PreviewPlayers();
13673
13674   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13675   {
13676     // first look for special settings configured in level series config
13677     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13678
13679     if (fileExists(filename_base))
13680       LoadMenuDesignSettingsFromFilename(filename_base);
13681   }
13682
13683   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13684
13685   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13686     LoadMenuDesignSettingsFromFilename(filename_local);
13687
13688   InitMenuDesignSettings_SpecialPostProcessing();
13689 }
13690
13691 void LoadMenuDesignSettings_AfterGraphics(void)
13692 {
13693   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13694 }
13695
13696 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13697                                 boolean ignore_defaults)
13698 {
13699   int i;
13700
13701   for (i = 0; sound_config_vars[i].token != NULL; i++)
13702   {
13703     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13704
13705     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13706     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13707       continue;
13708
13709     if (value != NULL)
13710       *sound_config_vars[i].value =
13711         get_token_parameter_value(sound_config_vars[i].token, value);
13712   }
13713 }
13714
13715 void InitSoundSettings_Static(void)
13716 {
13717   // always start with reliable default values from static default config
13718   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13719 }
13720
13721 static void LoadSoundSettingsFromFilename(char *filename)
13722 {
13723   SetupFileHash *setup_file_hash;
13724
13725   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13726     return;
13727
13728   // read (and overwrite with) values that may be specified in config file
13729   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13730
13731   freeSetupFileHash(setup_file_hash);
13732 }
13733
13734 void LoadSoundSettings(void)
13735 {
13736   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13737
13738   InitSoundSettings_Static();
13739
13740   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13741   {
13742     // first look for special settings configured in level series config
13743     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13744
13745     if (fileExists(filename_base))
13746       LoadSoundSettingsFromFilename(filename_base);
13747   }
13748
13749   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13750
13751   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13752     LoadSoundSettingsFromFilename(filename_local);
13753 }
13754
13755 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13756 {
13757   char *filename = getEditorSetupFilename();
13758   SetupFileList *setup_file_list, *list;
13759   SetupFileHash *element_hash;
13760   int num_unknown_tokens = 0;
13761   int i;
13762
13763   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13764     return;
13765
13766   element_hash = newSetupFileHash();
13767
13768   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13769     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13770
13771   // determined size may be larger than needed (due to unknown elements)
13772   *num_elements = 0;
13773   for (list = setup_file_list; list != NULL; list = list->next)
13774     (*num_elements)++;
13775
13776   // add space for up to 3 more elements for padding that may be needed
13777   *num_elements += 3;
13778
13779   // free memory for old list of elements, if needed
13780   checked_free(*elements);
13781
13782   // allocate memory for new list of elements
13783   *elements = checked_malloc(*num_elements * sizeof(int));
13784
13785   *num_elements = 0;
13786   for (list = setup_file_list; list != NULL; list = list->next)
13787   {
13788     char *value = getHashEntry(element_hash, list->token);
13789
13790     if (value == NULL)          // try to find obsolete token mapping
13791     {
13792       char *mapped_token = get_mapped_token(list->token);
13793
13794       if (mapped_token != NULL)
13795       {
13796         value = getHashEntry(element_hash, mapped_token);
13797
13798         free(mapped_token);
13799       }
13800     }
13801
13802     if (value != NULL)
13803     {
13804       (*elements)[(*num_elements)++] = atoi(value);
13805     }
13806     else
13807     {
13808       if (num_unknown_tokens == 0)
13809       {
13810         Warn("---");
13811         Warn("unknown token(s) found in config file:");
13812         Warn("- config file: '%s'", filename);
13813
13814         num_unknown_tokens++;
13815       }
13816
13817       Warn("- token: '%s'", list->token);
13818     }
13819   }
13820
13821   if (num_unknown_tokens > 0)
13822     Warn("---");
13823
13824   while (*num_elements % 4)     // pad with empty elements, if needed
13825     (*elements)[(*num_elements)++] = EL_EMPTY;
13826
13827   freeSetupFileList(setup_file_list);
13828   freeSetupFileHash(element_hash);
13829
13830 #if 0
13831   for (i = 0; i < *num_elements; i++)
13832     Debug("editor", "element '%s' [%d]\n",
13833           element_info[(*elements)[i]].token_name, (*elements)[i]);
13834 #endif
13835 }
13836
13837 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13838                                                      boolean is_sound)
13839 {
13840   SetupFileHash *setup_file_hash = NULL;
13841   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13842   char *filename_music, *filename_prefix, *filename_info;
13843   struct
13844   {
13845     char *token;
13846     char **value_ptr;
13847   }
13848   token_to_value_ptr[] =
13849   {
13850     { "title_header",   &tmp_music_file_info.title_header       },
13851     { "artist_header",  &tmp_music_file_info.artist_header      },
13852     { "album_header",   &tmp_music_file_info.album_header       },
13853     { "year_header",    &tmp_music_file_info.year_header        },
13854     { "played_header",  &tmp_music_file_info.played_header      },
13855
13856     { "title",          &tmp_music_file_info.title              },
13857     { "artist",         &tmp_music_file_info.artist             },
13858     { "album",          &tmp_music_file_info.album              },
13859     { "year",           &tmp_music_file_info.year               },
13860     { "played",         &tmp_music_file_info.played             },
13861
13862     { NULL,             NULL                                    },
13863   };
13864   int i;
13865
13866   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13867                     getCustomMusicFilename(basename));
13868
13869   if (filename_music == NULL)
13870     return NULL;
13871
13872   // ---------- try to replace file extension ----------
13873
13874   filename_prefix = getStringCopy(filename_music);
13875   if (strrchr(filename_prefix, '.') != NULL)
13876     *strrchr(filename_prefix, '.') = '\0';
13877   filename_info = getStringCat2(filename_prefix, ".txt");
13878
13879   if (fileExists(filename_info))
13880     setup_file_hash = loadSetupFileHash(filename_info);
13881
13882   free(filename_prefix);
13883   free(filename_info);
13884
13885   if (setup_file_hash == NULL)
13886   {
13887     // ---------- try to add file extension ----------
13888
13889     filename_prefix = getStringCopy(filename_music);
13890     filename_info = getStringCat2(filename_prefix, ".txt");
13891
13892     if (fileExists(filename_info))
13893       setup_file_hash = loadSetupFileHash(filename_info);
13894
13895     free(filename_prefix);
13896     free(filename_info);
13897   }
13898
13899   if (setup_file_hash == NULL)
13900     return NULL;
13901
13902   // ---------- music file info found ----------
13903
13904   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13905
13906   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13907   {
13908     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13909
13910     *token_to_value_ptr[i].value_ptr =
13911       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13912   }
13913
13914   tmp_music_file_info.basename = getStringCopy(basename);
13915   tmp_music_file_info.music = music;
13916   tmp_music_file_info.is_sound = is_sound;
13917
13918   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13919   *new_music_file_info = tmp_music_file_info;
13920
13921   return new_music_file_info;
13922 }
13923
13924 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13925 {
13926   return get_music_file_info_ext(basename, music, FALSE);
13927 }
13928
13929 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13930 {
13931   return get_music_file_info_ext(basename, sound, TRUE);
13932 }
13933
13934 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13935                                      char *basename, boolean is_sound)
13936 {
13937   for (; list != NULL; list = list->next)
13938     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13939       return TRUE;
13940
13941   return FALSE;
13942 }
13943
13944 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13945 {
13946   return music_info_listed_ext(list, basename, FALSE);
13947 }
13948
13949 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13950 {
13951   return music_info_listed_ext(list, basename, TRUE);
13952 }
13953
13954 void LoadMusicInfo(void)
13955 {
13956   int num_music_noconf = getMusicListSize_NoConf();
13957   int num_music = getMusicListSize();
13958   int num_sounds = getSoundListSize();
13959   struct FileInfo *music, *sound;
13960   struct MusicFileInfo *next, **new;
13961
13962   int i;
13963
13964   while (music_file_info != NULL)
13965   {
13966     next = music_file_info->next;
13967
13968     checked_free(music_file_info->basename);
13969
13970     checked_free(music_file_info->title_header);
13971     checked_free(music_file_info->artist_header);
13972     checked_free(music_file_info->album_header);
13973     checked_free(music_file_info->year_header);
13974     checked_free(music_file_info->played_header);
13975
13976     checked_free(music_file_info->title);
13977     checked_free(music_file_info->artist);
13978     checked_free(music_file_info->album);
13979     checked_free(music_file_info->year);
13980     checked_free(music_file_info->played);
13981
13982     free(music_file_info);
13983
13984     music_file_info = next;
13985   }
13986
13987   new = &music_file_info;
13988
13989   // get (configured or unconfigured) music file info for all levels
13990   for (i = leveldir_current->first_level;
13991        i <= leveldir_current->last_level; i++)
13992   {
13993     int music_nr;
13994
13995     if (levelset.music[i] != MUS_UNDEFINED)
13996     {
13997       // get music file info for configured level music
13998       music_nr = levelset.music[i];
13999     }
14000     else if (num_music_noconf > 0)
14001     {
14002       // get music file info for unconfigured level music
14003       int level_pos = i - leveldir_current->first_level;
14004
14005       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14006     }
14007     else
14008     {
14009       continue;
14010     }
14011
14012     char *basename = getMusicInfoEntryFilename(music_nr);
14013
14014     if (basename == NULL)
14015       continue;
14016
14017     if (!music_info_listed(music_file_info, basename))
14018     {
14019       *new = get_music_file_info(basename, music_nr);
14020
14021       if (*new != NULL)
14022         new = &(*new)->next;
14023     }
14024   }
14025
14026   // get music file info for all remaining configured music files
14027   for (i = 0; i < num_music; i++)
14028   {
14029     music = getMusicListEntry(i);
14030
14031     if (music->filename == NULL)
14032       continue;
14033
14034     if (strEqual(music->filename, UNDEFINED_FILENAME))
14035       continue;
14036
14037     // a configured file may be not recognized as music
14038     if (!FileIsMusic(music->filename))
14039       continue;
14040
14041     if (!music_info_listed(music_file_info, music->filename))
14042     {
14043       *new = get_music_file_info(music->filename, i);
14044
14045       if (*new != NULL)
14046         new = &(*new)->next;
14047     }
14048   }
14049
14050   // get sound file info for all configured sound files
14051   for (i = 0; i < num_sounds; i++)
14052   {
14053     sound = getSoundListEntry(i);
14054
14055     if (sound->filename == NULL)
14056       continue;
14057
14058     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14059       continue;
14060
14061     // a configured file may be not recognized as sound
14062     if (!FileIsSound(sound->filename))
14063       continue;
14064
14065     if (!sound_info_listed(music_file_info, sound->filename))
14066     {
14067       *new = get_sound_file_info(sound->filename, i);
14068       if (*new != NULL)
14069         new = &(*new)->next;
14070     }
14071   }
14072
14073   // add pointers to previous list nodes
14074
14075   struct MusicFileInfo *node = music_file_info;
14076
14077   while (node != NULL)
14078   {
14079     if (node->next)
14080       node->next->prev = node;
14081
14082     node = node->next;
14083   }
14084 }
14085
14086 static void add_helpanim_entry(int element, int action, int direction,
14087                                int delay, int *num_list_entries)
14088 {
14089   struct HelpAnimInfo *new_list_entry;
14090   (*num_list_entries)++;
14091
14092   helpanim_info =
14093     checked_realloc(helpanim_info,
14094                     *num_list_entries * sizeof(struct HelpAnimInfo));
14095   new_list_entry = &helpanim_info[*num_list_entries - 1];
14096
14097   new_list_entry->element = element;
14098   new_list_entry->action = action;
14099   new_list_entry->direction = direction;
14100   new_list_entry->delay = delay;
14101 }
14102
14103 static void print_unknown_token(char *filename, char *token, int token_nr)
14104 {
14105   if (token_nr == 0)
14106   {
14107     Warn("---");
14108     Warn("unknown token(s) found in config file:");
14109     Warn("- config file: '%s'", filename);
14110   }
14111
14112   Warn("- token: '%s'", token);
14113 }
14114
14115 static void print_unknown_token_end(int token_nr)
14116 {
14117   if (token_nr > 0)
14118     Warn("---");
14119 }
14120
14121 void LoadHelpAnimInfo(void)
14122 {
14123   char *filename = getHelpAnimFilename();
14124   SetupFileList *setup_file_list = NULL, *list;
14125   SetupFileHash *element_hash, *action_hash, *direction_hash;
14126   int num_list_entries = 0;
14127   int num_unknown_tokens = 0;
14128   int i;
14129
14130   if (fileExists(filename))
14131     setup_file_list = loadSetupFileList(filename);
14132
14133   if (setup_file_list == NULL)
14134   {
14135     // use reliable default values from static configuration
14136     SetupFileList *insert_ptr;
14137
14138     insert_ptr = setup_file_list =
14139       newSetupFileList(helpanim_config[0].token,
14140                        helpanim_config[0].value);
14141
14142     for (i = 1; helpanim_config[i].token; i++)
14143       insert_ptr = addListEntry(insert_ptr,
14144                                 helpanim_config[i].token,
14145                                 helpanim_config[i].value);
14146   }
14147
14148   element_hash   = newSetupFileHash();
14149   action_hash    = newSetupFileHash();
14150   direction_hash = newSetupFileHash();
14151
14152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14153     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14154
14155   for (i = 0; i < NUM_ACTIONS; i++)
14156     setHashEntry(action_hash, element_action_info[i].suffix,
14157                  i_to_a(element_action_info[i].value));
14158
14159   // do not store direction index (bit) here, but direction value!
14160   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14161     setHashEntry(direction_hash, element_direction_info[i].suffix,
14162                  i_to_a(1 << element_direction_info[i].value));
14163
14164   for (list = setup_file_list; list != NULL; list = list->next)
14165   {
14166     char *element_token, *action_token, *direction_token;
14167     char *element_value, *action_value, *direction_value;
14168     int delay = atoi(list->value);
14169
14170     if (strEqual(list->token, "end"))
14171     {
14172       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14173
14174       continue;
14175     }
14176
14177     /* first try to break element into element/action/direction parts;
14178        if this does not work, also accept combined "element[.act][.dir]"
14179        elements (like "dynamite.active"), which are unique elements */
14180
14181     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14182     {
14183       element_value = getHashEntry(element_hash, list->token);
14184       if (element_value != NULL)        // element found
14185         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14186                            &num_list_entries);
14187       else
14188       {
14189         // no further suffixes found -- this is not an element
14190         print_unknown_token(filename, list->token, num_unknown_tokens++);
14191       }
14192
14193       continue;
14194     }
14195
14196     // token has format "<prefix>.<something>"
14197
14198     action_token = strchr(list->token, '.');    // suffix may be action ...
14199     direction_token = action_token;             // ... or direction
14200
14201     element_token = getStringCopy(list->token);
14202     *strchr(element_token, '.') = '\0';
14203
14204     element_value = getHashEntry(element_hash, element_token);
14205
14206     if (element_value == NULL)          // this is no element
14207     {
14208       element_value = getHashEntry(element_hash, list->token);
14209       if (element_value != NULL)        // combined element found
14210         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14211                            &num_list_entries);
14212       else
14213         print_unknown_token(filename, list->token, num_unknown_tokens++);
14214
14215       free(element_token);
14216
14217       continue;
14218     }
14219
14220     action_value = getHashEntry(action_hash, action_token);
14221
14222     if (action_value != NULL)           // action found
14223     {
14224       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14225                     &num_list_entries);
14226
14227       free(element_token);
14228
14229       continue;
14230     }
14231
14232     direction_value = getHashEntry(direction_hash, direction_token);
14233
14234     if (direction_value != NULL)        // direction found
14235     {
14236       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14237                          &num_list_entries);
14238
14239       free(element_token);
14240
14241       continue;
14242     }
14243
14244     if (strchr(action_token + 1, '.') == NULL)
14245     {
14246       // no further suffixes found -- this is not an action nor direction
14247
14248       element_value = getHashEntry(element_hash, list->token);
14249       if (element_value != NULL)        // combined element found
14250         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14251                            &num_list_entries);
14252       else
14253         print_unknown_token(filename, list->token, num_unknown_tokens++);
14254
14255       free(element_token);
14256
14257       continue;
14258     }
14259
14260     // token has format "<prefix>.<suffix>.<something>"
14261
14262     direction_token = strchr(action_token + 1, '.');
14263
14264     action_token = getStringCopy(action_token);
14265     *strchr(action_token + 1, '.') = '\0';
14266
14267     action_value = getHashEntry(action_hash, action_token);
14268
14269     if (action_value == NULL)           // this is no action
14270     {
14271       element_value = getHashEntry(element_hash, list->token);
14272       if (element_value != NULL)        // combined element found
14273         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14274                            &num_list_entries);
14275       else
14276         print_unknown_token(filename, list->token, num_unknown_tokens++);
14277
14278       free(element_token);
14279       free(action_token);
14280
14281       continue;
14282     }
14283
14284     direction_value = getHashEntry(direction_hash, direction_token);
14285
14286     if (direction_value != NULL)        // direction found
14287     {
14288       add_helpanim_entry(atoi(element_value), atoi(action_value),
14289                          atoi(direction_value), delay, &num_list_entries);
14290
14291       free(element_token);
14292       free(action_token);
14293
14294       continue;
14295     }
14296
14297     // this is no direction
14298
14299     element_value = getHashEntry(element_hash, list->token);
14300     if (element_value != NULL)          // combined element found
14301       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14302                          &num_list_entries);
14303     else
14304       print_unknown_token(filename, list->token, num_unknown_tokens++);
14305
14306     free(element_token);
14307     free(action_token);
14308   }
14309
14310   print_unknown_token_end(num_unknown_tokens);
14311
14312   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14313   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14314
14315   freeSetupFileList(setup_file_list);
14316   freeSetupFileHash(element_hash);
14317   freeSetupFileHash(action_hash);
14318   freeSetupFileHash(direction_hash);
14319
14320 #if 0
14321   for (i = 0; i < num_list_entries; i++)
14322     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14323           EL_NAME(helpanim_info[i].element),
14324           helpanim_info[i].element,
14325           helpanim_info[i].action,
14326           helpanim_info[i].direction,
14327           helpanim_info[i].delay);
14328 #endif
14329 }
14330
14331 void LoadHelpTextInfo(void)
14332 {
14333   char *filename = getHelpTextFilename();
14334   int i;
14335
14336   if (helptext_info != NULL)
14337   {
14338     freeSetupFileHash(helptext_info);
14339     helptext_info = NULL;
14340   }
14341
14342   if (fileExists(filename))
14343     helptext_info = loadSetupFileHash(filename);
14344
14345   if (helptext_info == NULL)
14346   {
14347     // use reliable default values from static configuration
14348     helptext_info = newSetupFileHash();
14349
14350     for (i = 0; helptext_config[i].token; i++)
14351       setHashEntry(helptext_info,
14352                    helptext_config[i].token,
14353                    helptext_config[i].value);
14354   }
14355
14356 #if 0
14357   BEGIN_HASH_ITERATION(helptext_info, itr)
14358   {
14359     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14360           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14361   }
14362   END_HASH_ITERATION(hash, itr)
14363 #endif
14364 }
14365
14366
14367 // ----------------------------------------------------------------------------
14368 // convert levels
14369 // ----------------------------------------------------------------------------
14370
14371 #define MAX_NUM_CONVERT_LEVELS          1000
14372
14373 void ConvertLevels(void)
14374 {
14375   static LevelDirTree *convert_leveldir = NULL;
14376   static int convert_level_nr = -1;
14377   static int num_levels_handled = 0;
14378   static int num_levels_converted = 0;
14379   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14380   int i;
14381
14382   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14383                                                global.convert_leveldir);
14384
14385   if (convert_leveldir == NULL)
14386     Fail("no such level identifier: '%s'", global.convert_leveldir);
14387
14388   leveldir_current = convert_leveldir;
14389
14390   if (global.convert_level_nr != -1)
14391   {
14392     convert_leveldir->first_level = global.convert_level_nr;
14393     convert_leveldir->last_level  = global.convert_level_nr;
14394   }
14395
14396   convert_level_nr = convert_leveldir->first_level;
14397
14398   PrintLine("=", 79);
14399   Print("Converting levels\n");
14400   PrintLine("-", 79);
14401   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14402   Print("Level series name:       '%s'\n", convert_leveldir->name);
14403   Print("Level series author:     '%s'\n", convert_leveldir->author);
14404   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14405   PrintLine("=", 79);
14406   Print("\n");
14407
14408   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14409     levels_failed[i] = FALSE;
14410
14411   while (convert_level_nr <= convert_leveldir->last_level)
14412   {
14413     char *level_filename;
14414     boolean new_level;
14415
14416     level_nr = convert_level_nr++;
14417
14418     Print("Level %03d: ", level_nr);
14419
14420     LoadLevel(level_nr);
14421     if (level.no_level_file || level.no_valid_file)
14422     {
14423       Print("(no level)\n");
14424       continue;
14425     }
14426
14427     Print("converting level ... ");
14428
14429 #if 0
14430     // special case: conversion of some EMC levels as requested by ACME
14431     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14432 #endif
14433
14434     level_filename = getDefaultLevelFilename(level_nr);
14435     new_level = !fileExists(level_filename);
14436
14437     if (new_level)
14438     {
14439       SaveLevel(level_nr);
14440
14441       num_levels_converted++;
14442
14443       Print("converted.\n");
14444     }
14445     else
14446     {
14447       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14448         levels_failed[level_nr] = TRUE;
14449
14450       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14451     }
14452
14453     num_levels_handled++;
14454   }
14455
14456   Print("\n");
14457   PrintLine("=", 79);
14458   Print("Number of levels handled: %d\n", num_levels_handled);
14459   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14460          (num_levels_handled ?
14461           num_levels_converted * 100 / num_levels_handled : 0));
14462   PrintLine("-", 79);
14463   Print("Summary (for automatic parsing by scripts):\n");
14464   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14465          convert_leveldir->identifier, num_levels_converted,
14466          num_levels_handled,
14467          (num_levels_handled ?
14468           num_levels_converted * 100 / num_levels_handled : 0));
14469
14470   if (num_levels_handled != num_levels_converted)
14471   {
14472     Print(", FAILED:");
14473     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14474       if (levels_failed[i])
14475         Print(" %03d", i);
14476   }
14477
14478   Print("\n");
14479   PrintLine("=", 79);
14480
14481   CloseAllAndExit(0);
14482 }
14483
14484
14485 // ----------------------------------------------------------------------------
14486 // create and save images for use in level sketches (raw BMP format)
14487 // ----------------------------------------------------------------------------
14488
14489 void CreateLevelSketchImages(void)
14490 {
14491   Bitmap *bitmap1;
14492   Bitmap *bitmap2;
14493   int i;
14494
14495   InitElementPropertiesGfxElement();
14496
14497   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14498   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14499
14500   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14501   {
14502     int element = getMappedElement(i);
14503     char basename1[16];
14504     char basename2[16];
14505     char *filename1;
14506     char *filename2;
14507
14508     sprintf(basename1, "%04d.bmp", i);
14509     sprintf(basename2, "%04ds.bmp", i);
14510
14511     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14512     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14513
14514     DrawSizedElement(0, 0, element, TILESIZE);
14515     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14516
14517     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14518       Fail("cannot save level sketch image file '%s'", filename1);
14519
14520     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14521     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14522
14523     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14524       Fail("cannot save level sketch image file '%s'", filename2);
14525
14526     free(filename1);
14527     free(filename2);
14528
14529     // create corresponding SQL statements (for normal and small images)
14530     if (i < 1000)
14531     {
14532       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14533       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14534     }
14535
14536     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14537     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14538
14539     // optional: create content for forum level sketch demonstration post
14540     if (options.debug)
14541       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14542   }
14543
14544   FreeBitmap(bitmap1);
14545   FreeBitmap(bitmap2);
14546
14547   if (options.debug)
14548     fprintf(stderr, "\n");
14549
14550   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14551
14552   CloseAllAndExit(0);
14553 }
14554
14555
14556 // ----------------------------------------------------------------------------
14557 // create and save images for element collecting animations (raw BMP format)
14558 // ----------------------------------------------------------------------------
14559
14560 static boolean createCollectImage(int element)
14561 {
14562   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14563 }
14564
14565 void CreateCollectElementImages(void)
14566 {
14567   int i, j;
14568   int num_steps = 8;
14569   int anim_frames = num_steps - 1;
14570   int tile_size = TILESIZE;
14571   int anim_width  = tile_size * anim_frames;
14572   int anim_height = tile_size;
14573   int num_collect_images = 0;
14574   int pos_collect_images = 0;
14575
14576   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14577     if (createCollectImage(i))
14578       num_collect_images++;
14579
14580   Info("Creating %d element collecting animation images ...",
14581        num_collect_images);
14582
14583   int dst_width  = anim_width * 2;
14584   int dst_height = anim_height * num_collect_images / 2;
14585   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14586   char *basename_bmp = "RocksCollect.bmp";
14587   char *basename_png = "RocksCollect.png";
14588   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14589   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14590   int len_filename_bmp = strlen(filename_bmp);
14591   int len_filename_png = strlen(filename_png);
14592   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14593   char cmd_convert[max_command_len];
14594
14595   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14596            filename_bmp,
14597            filename_png);
14598
14599   // force using RGBA surface for destination bitmap
14600   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14601                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14602
14603   dst_bitmap->surface =
14604     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14605
14606   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14607   {
14608     if (!createCollectImage(i))
14609       continue;
14610
14611     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14612     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14613     int graphic = el2img(i);
14614     char *token_name = element_info[i].token_name;
14615     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14616     Bitmap *src_bitmap;
14617     int src_x, src_y;
14618
14619     Info("- creating collecting image for '%s' ...", token_name);
14620
14621     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14622
14623     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14624                tile_size, tile_size, 0, 0);
14625
14626     // force using RGBA surface for temporary bitmap (using transparent black)
14627     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14628                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14629
14630     tmp_bitmap->surface =
14631       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14632
14633     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14634
14635     for (j = 0; j < anim_frames; j++)
14636     {
14637       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14638       int frame_size = frame_size_final * num_steps;
14639       int offset = (tile_size - frame_size_final) / 2;
14640       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14641
14642       while (frame_size > frame_size_final)
14643       {
14644         frame_size /= 2;
14645
14646         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14647
14648         FreeBitmap(frame_bitmap);
14649
14650         frame_bitmap = half_bitmap;
14651       }
14652
14653       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14654                        frame_size_final, frame_size_final,
14655                        dst_x + j * tile_size + offset, dst_y + offset);
14656
14657       FreeBitmap(frame_bitmap);
14658     }
14659
14660     tmp_bitmap->surface_masked = NULL;
14661
14662     FreeBitmap(tmp_bitmap);
14663
14664     pos_collect_images++;
14665   }
14666
14667   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14668     Fail("cannot save element collecting image file '%s'", filename_bmp);
14669
14670   FreeBitmap(dst_bitmap);
14671
14672   Info("Converting image file from BMP to PNG ...");
14673
14674   if (system(cmd_convert) != 0)
14675     Fail("converting image file failed");
14676
14677   unlink(filename_bmp);
14678
14679   Info("Done.");
14680
14681   CloseAllAndExit(0);
14682 }
14683
14684
14685 // ----------------------------------------------------------------------------
14686 // create and save images for custom and group elements (raw BMP format)
14687 // ----------------------------------------------------------------------------
14688
14689 void CreateCustomElementImages(char *directory)
14690 {
14691   char *src_basename = "RocksCE-template.ilbm";
14692   char *dst_basename = "RocksCE.bmp";
14693   char *src_filename = getPath2(directory, src_basename);
14694   char *dst_filename = getPath2(directory, dst_basename);
14695   Bitmap *src_bitmap;
14696   Bitmap *bitmap;
14697   int yoffset_ce = 0;
14698   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14699   int i;
14700
14701   InitVideoDefaults();
14702
14703   ReCreateBitmap(&backbuffer, video.width, video.height);
14704
14705   src_bitmap = LoadImage(src_filename);
14706
14707   bitmap = CreateBitmap(TILEX * 16 * 2,
14708                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14709                         DEFAULT_DEPTH);
14710
14711   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14712   {
14713     int x = i % 16;
14714     int y = i / 16;
14715     int ii = i + 1;
14716     int j;
14717
14718     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14719                TILEX * x, TILEY * y + yoffset_ce);
14720
14721     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14722                TILEX, TILEY,
14723                TILEX * x + TILEX * 16,
14724                TILEY * y + yoffset_ce);
14725
14726     for (j = 2; j >= 0; j--)
14727     {
14728       int c = ii % 10;
14729
14730       BlitBitmap(src_bitmap, bitmap,
14731                  TILEX + c * 7, 0, 6, 10,
14732                  TILEX * x + 6 + j * 7,
14733                  TILEY * y + 11 + yoffset_ce);
14734
14735       BlitBitmap(src_bitmap, bitmap,
14736                  TILEX + c * 8, TILEY, 6, 10,
14737                  TILEX * 16 + TILEX * x + 6 + j * 8,
14738                  TILEY * y + 10 + yoffset_ce);
14739
14740       ii /= 10;
14741     }
14742   }
14743
14744   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14745   {
14746     int x = i % 16;
14747     int y = i / 16;
14748     int ii = i + 1;
14749     int j;
14750
14751     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14752                TILEX * x, TILEY * y + yoffset_ge);
14753
14754     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14755                TILEX, TILEY,
14756                TILEX * x + TILEX * 16,
14757                TILEY * y + yoffset_ge);
14758
14759     for (j = 1; j >= 0; j--)
14760     {
14761       int c = ii % 10;
14762
14763       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14764                  TILEX * x + 6 + j * 10,
14765                  TILEY * y + 11 + yoffset_ge);
14766
14767       BlitBitmap(src_bitmap, bitmap,
14768                  TILEX + c * 8, TILEY + 12, 6, 10,
14769                  TILEX * 16 + TILEX * x + 10 + j * 8,
14770                  TILEY * y + 10 + yoffset_ge);
14771
14772       ii /= 10;
14773     }
14774   }
14775
14776   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14777     Fail("cannot save CE graphics file '%s'", dst_filename);
14778
14779   FreeBitmap(bitmap);
14780
14781   CloseAllAndExit(0);
14782 }