added preprocessor macros to improve code readability
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318
319   {
320     -1,                                 -1,
321     -1,                                 -1,
322     NULL,                               -1
323   }
324 };
325
326 static struct LevelFileConfigInfo chunk_config_ELEM[] =
327 {
328   // (these values are the same for each player)
329   {
330     EL_PLAYER_1,                        -1,
331     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
332     &li.block_last_field,               FALSE   // default case for EM levels
333   },
334   {
335     EL_PLAYER_1,                        -1,
336     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
337     &li.sp_block_last_field,            TRUE    // default case for SP levels
338   },
339   {
340     EL_PLAYER_1,                        -1,
341     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
342     &li.instant_relocation,             FALSE
343   },
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
347     &li.can_pass_to_walkable,           FALSE
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
352     &li.block_snap_field,               TRUE
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
357     &li.continuous_snapping,            TRUE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
362     &li.shifted_relocation,             FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
367     &li.lazy_relocation,                FALSE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
372     &li.finish_dig_collect,             TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
377     &li.keep_walkable_ce,               FALSE
378   },
379
380   // (these values are different for each player)
381   {
382     EL_PLAYER_1,                        -1,
383     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
384     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
385   },
386   {
387     EL_PLAYER_1,                        -1,
388     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
389     &li.initial_player_gravity[0],      FALSE
390   },
391   {
392     EL_PLAYER_1,                        -1,
393     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
394     &li.use_start_element[0],           FALSE
395   },
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
399     &li.start_element[0],               EL_PLAYER_1
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
404     &li.use_artwork_element[0],         FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
409     &li.artwork_element[0],             EL_PLAYER_1
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
414     &li.use_explosion_element[0],       FALSE
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
419     &li.explosion_element[0],           EL_PLAYER_1
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
424     &li.use_initial_inventory[0],       FALSE
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
429     &li.initial_inventory_size[0],      1
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
434     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
435     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
436   },
437
438   {
439     EL_PLAYER_2,                        -1,
440     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
441     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
442   },
443   {
444     EL_PLAYER_2,                        -1,
445     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
446     &li.initial_player_gravity[1],      FALSE
447   },
448   {
449     EL_PLAYER_2,                        -1,
450     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
451     &li.use_start_element[1],           FALSE
452   },
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
456     &li.start_element[1],               EL_PLAYER_2
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
461     &li.use_artwork_element[1],         FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
466     &li.artwork_element[1],             EL_PLAYER_2
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
471     &li.use_explosion_element[1],       FALSE
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
476     &li.explosion_element[1],           EL_PLAYER_2
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
481     &li.use_initial_inventory[1],       FALSE
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
486     &li.initial_inventory_size[1],      1
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
491     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
492     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
493   },
494
495   {
496     EL_PLAYER_3,                        -1,
497     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
498     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
499   },
500   {
501     EL_PLAYER_3,                        -1,
502     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
503     &li.initial_player_gravity[2],      FALSE
504   },
505   {
506     EL_PLAYER_3,                        -1,
507     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
508     &li.use_start_element[2],           FALSE
509   },
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
513     &li.start_element[2],               EL_PLAYER_3
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
518     &li.use_artwork_element[2],         FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
523     &li.artwork_element[2],             EL_PLAYER_3
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
528     &li.use_explosion_element[2],       FALSE
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
533     &li.explosion_element[2],           EL_PLAYER_3
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
538     &li.use_initial_inventory[2],       FALSE
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
543     &li.initial_inventory_size[2],      1
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
548     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
549     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
550   },
551
552   {
553     EL_PLAYER_4,                        -1,
554     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
555     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
556   },
557   {
558     EL_PLAYER_4,                        -1,
559     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
560     &li.initial_player_gravity[3],      FALSE
561   },
562   {
563     EL_PLAYER_4,                        -1,
564     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
565     &li.use_start_element[3],           FALSE
566   },
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
570     &li.start_element[3],               EL_PLAYER_4
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
575     &li.use_artwork_element[3],         FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
580     &li.artwork_element[3],             EL_PLAYER_4
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
585     &li.use_explosion_element[3],       FALSE
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
590     &li.explosion_element[3],           EL_PLAYER_4
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
595     &li.use_initial_inventory[3],       FALSE
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
600     &li.initial_inventory_size[3],      1
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
605     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
606     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
607   },
608
609   // (these values are only valid for BD style levels)
610   // (some values for BD style amoeba following below)
611   {
612     EL_BD_PLAYER,                       -1,
613     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
614     &li.bd_diagonal_movements,          FALSE
615   },
616   {
617     EL_BD_PLAYER,                       -1,
618     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
619     &li.bd_topmost_player_active,       TRUE
620   },
621   {
622     EL_BD_PLAYER,                       -1,
623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
624     &li.bd_pushing_prob,                25
625   },
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
629     &li.bd_pushing_prob_with_sweet,     100
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
634     &li.bd_push_mega_rock_with_sweet,   FALSE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
639     &li.bd_snap_element,                EL_EMPTY
640   },
641
642   {
643     EL_BD_DIAMOND,                      -1,
644     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
645     &li.score[SC_DIAMOND_EXTRA],        20
646   },
647
648   {
649     EL_BD_MAGIC_WALL,                   -1,
650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
651     &li.bd_magic_wall_wait_hatching,    FALSE
652   },
653   {
654     EL_BD_MAGIC_WALL,                   -1,
655     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
656     &li.bd_magic_wall_stops_amoeba,     TRUE
657   },
658   {
659     EL_BD_MAGIC_WALL,                   -1,
660     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
661     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
662   },
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
666     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
671     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
676     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
681     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
686     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
691     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
692   },
693
694   {
695     EL_BD_CLOCK,                        -1,
696     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
697     &li.bd_clock_extra_time,            30
698   },
699
700   {
701     EL_BD_VOODOO_DOLL,                  -1,
702     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
703     &li.bd_voodoo_collects_diamonds,    FALSE
704   },
705   {
706     EL_BD_VOODOO_DOLL,                  -1,
707     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
708     &li.bd_voodoo_hurt_kills_player,    FALSE
709   },
710   {
711     EL_BD_VOODOO_DOLL,                  -1,
712     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
713     &li.bd_voodoo_dies_by_rock,         FALSE
714   },
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
718     &li.bd_voodoo_vanish_by_explosion,  TRUE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
723     &li.bd_voodoo_penalty_time,         30
724   },
725
726   {
727     EL_BD_SLIME,                        -1,
728     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
729     &li.bd_slime_is_predictable,        TRUE
730   },
731   {
732     EL_BD_SLIME,                        -1,
733     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
734     &li.bd_slime_permeability_rate,     100
735   },
736   {
737     EL_BD_SLIME,                        -1,
738     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
739     &li.bd_slime_permeability_bits_c64, 0
740   },
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
744     &li.bd_slime_random_seed_c64,       -1
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
749     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
754     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
759     &li.bd_slime_eats_element_2,        EL_BD_ROCK
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
764     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
769     &li.bd_slime_eats_element_3,        EL_BD_NUT
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
774     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
775   },
776
777   {
778     EL_BD_ACID,                         -1,
779     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
780     &li.bd_acid_eats_element,           EL_BD_SAND
781   },
782   {
783     EL_BD_ACID,                         -1,
784     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
785     &li.bd_acid_spread_rate,            3
786   },
787   {
788     EL_BD_ACID,                         -1,
789     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
790     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
791   },
792
793   {
794     EL_BD_BITER,                        -1,
795     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
796     &li.bd_biter_move_delay,            0
797   },
798   {
799     EL_BD_BITER,                        -1,
800     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
801     &li.bd_biter_eats_element,          EL_BD_DIAMOND
802   },
803
804   {
805     EL_BD_BLADDER,                      -1,
806     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
807     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
808   },
809
810   {
811     EL_BD_EXPANDABLE_WALL_ANY,          -1,
812     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
813     &li.bd_change_expanding_wall,       FALSE
814   },
815   {
816     EL_BD_EXPANDABLE_WALL_ANY,          -1,
817     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
818     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
819   },
820
821   {
822     EL_BD_REPLICATOR,                   -1,
823     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
824     &li.bd_replicators_active,          TRUE
825   },
826   {
827     EL_BD_REPLICATOR,                   -1,
828     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
829     &li.bd_replicator_create_delay,     4
830   },
831
832   {
833     EL_BD_CONVEYOR_LEFT,                -1,
834     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
835     &li.bd_conveyor_belts_active,       TRUE
836   },
837   {
838     EL_BD_CONVEYOR_LEFT,                -1,
839     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
840     &li.bd_conveyor_belts_changed,      FALSE
841   },
842
843   {
844     EL_BD_WATER,                        -1,
845     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
846     &li.bd_water_cannot_flow_down,      FALSE
847   },
848
849   {
850     EL_BD_NUT,                          -1,
851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
852     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
853   },
854
855   {
856     EL_BD_PNEUMATIC_HAMMER,             -1,
857     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
858     &li.bd_hammer_walls_break_delay,    5
859   },
860   {
861     EL_BD_PNEUMATIC_HAMMER,             -1,
862     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
863     &li.bd_hammer_walls_reappear,       FALSE
864   },
865   {
866     EL_BD_PNEUMATIC_HAMMER,             -1,
867     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
868     &li.bd_hammer_walls_reappear_delay, 100
869   },
870
871   {
872     EL_BD_SKELETON,                     -1,
873     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
874     &li.bd_num_skeletons_needed_for_pot, 5
875   },
876   {
877     EL_BD_SKELETON,                     -1,
878     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
879     &li.bd_skeleton_worth_num_diamonds, 0
880   },
881
882   {
883     EL_BD_SAND,                         -1,
884     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
885     &li.bd_sand_looks_like,             EL_BD_SAND
886   },
887
888   // (the following values are related to various game elements)
889
890   {
891     EL_EMERALD,                         -1,
892     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
893     &li.score[SC_EMERALD],              10
894   },
895
896   {
897     EL_DIAMOND,                         -1,
898     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
899     &li.score[SC_DIAMOND],              10
900   },
901
902   {
903     EL_BUG,                             -1,
904     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
905     &li.score[SC_BUG],                  10
906   },
907
908   {
909     EL_SPACESHIP,                       -1,
910     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
911     &li.score[SC_SPACESHIP],            10
912   },
913
914   {
915     EL_PACMAN,                          -1,
916     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
917     &li.score[SC_PACMAN],               10
918   },
919
920   {
921     EL_NUT,                             -1,
922     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
923     &li.score[SC_NUT],                  10
924   },
925
926   {
927     EL_DYNAMITE,                        -1,
928     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
929     &li.score[SC_DYNAMITE],             10
930   },
931
932   {
933     EL_KEY_1,                           -1,
934     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
935     &li.score[SC_KEY],                  10
936   },
937
938   {
939     EL_PEARL,                           -1,
940     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
941     &li.score[SC_PEARL],                10
942   },
943
944   {
945     EL_CRYSTAL,                         -1,
946     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
947     &li.score[SC_CRYSTAL],              10
948   },
949
950   // (amoeba values used by R'n'D game engine only)
951   {
952     EL_BD_AMOEBA,                       -1,
953     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
954     &li.amoeba_content,                 EL_DIAMOND
955   },
956   {
957     EL_BD_AMOEBA,                       -1,
958     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
959     &li.amoeba_speed,                   10
960   },
961   {
962     EL_BD_AMOEBA,                       -1,
963     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
964     &li.grow_into_diggable,             TRUE
965   },
966   // (amoeba values used by BD game engine only)
967   {
968     EL_BD_AMOEBA,                       -1,
969     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
970     &li.bd_amoeba_wait_for_hatching,    FALSE
971   },
972   {
973     EL_BD_AMOEBA,                       -1,
974     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
975     &li.bd_amoeba_start_immediately,    TRUE
976   },
977   {
978     EL_BD_AMOEBA,                       -1,
979     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
980     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
981   },
982   {
983     EL_BD_AMOEBA,                       -1,
984     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
985     &li.bd_amoeba_threshold_too_big,    200
986   },
987   {
988     EL_BD_AMOEBA,                       -1,
989     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
990     &li.bd_amoeba_slow_growth_time,     200
991   },
992   {
993     EL_BD_AMOEBA,                       -1,
994     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
995     &li.bd_amoeba_slow_growth_rate,     3
996   },
997   {
998     EL_BD_AMOEBA,                       -1,
999     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1000     &li.bd_amoeba_fast_growth_rate,     25
1001   },
1002   {
1003     EL_BD_AMOEBA,                       -1,
1004     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1005     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1006   },
1007   {
1008     EL_BD_AMOEBA,                       -1,
1009     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1010     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1011   },
1012
1013   {
1014     EL_BD_AMOEBA_2,                     -1,
1015     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1016     &li.bd_amoeba_2_threshold_too_big,  200
1017   },
1018   {
1019     EL_BD_AMOEBA_2,                     -1,
1020     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1021     &li.bd_amoeba_2_slow_growth_time,   200
1022   },
1023   {
1024     EL_BD_AMOEBA_2,                     -1,
1025     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1026     &li.bd_amoeba_2_slow_growth_rate,   3
1027   },
1028   {
1029     EL_BD_AMOEBA_2,                     -1,
1030     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1031     &li.bd_amoeba_2_fast_growth_rate,   25
1032   },
1033   {
1034     EL_BD_AMOEBA_2,                     -1,
1035     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1036     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1037   },
1038   {
1039     EL_BD_AMOEBA_2,                     -1,
1040     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1041     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1042   },
1043   {
1044     EL_BD_AMOEBA_2,                     -1,
1045     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1046     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1047   },
1048   {
1049     EL_BD_AMOEBA_2,                     -1,
1050     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1051     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1052   },
1053
1054   {
1055     EL_YAMYAM,                          -1,
1056     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1057     &li.yamyam_content,                 EL_ROCK, NULL,
1058     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1059   },
1060   {
1061     EL_YAMYAM,                          -1,
1062     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1063     &li.score[SC_YAMYAM],               10
1064   },
1065
1066   {
1067     EL_ROBOT,                           -1,
1068     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1069     &li.score[SC_ROBOT],                10
1070   },
1071   {
1072     EL_ROBOT,                           -1,
1073     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1074     &li.slurp_score,                    10
1075   },
1076
1077   {
1078     EL_ROBOT_WHEEL,                     -1,
1079     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1080     &li.time_wheel,                     10
1081   },
1082
1083   {
1084     EL_MAGIC_WALL,                      -1,
1085     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1086     &li.time_magic_wall,                10
1087   },
1088
1089   {
1090     EL_GAME_OF_LIFE,                    -1,
1091     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1092     &li.game_of_life[0],                2
1093   },
1094   {
1095     EL_GAME_OF_LIFE,                    -1,
1096     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1097     &li.game_of_life[1],                3
1098   },
1099   {
1100     EL_GAME_OF_LIFE,                    -1,
1101     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1102     &li.game_of_life[2],                3
1103   },
1104   {
1105     EL_GAME_OF_LIFE,                    -1,
1106     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1107     &li.game_of_life[3],                3
1108   },
1109   {
1110     EL_GAME_OF_LIFE,                    -1,
1111     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1112     &li.use_life_bugs,                  FALSE
1113   },
1114
1115   {
1116     EL_BIOMAZE,                         -1,
1117     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1118     &li.biomaze[0],                     2
1119   },
1120   {
1121     EL_BIOMAZE,                         -1,
1122     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1123     &li.biomaze[1],                     3
1124   },
1125   {
1126     EL_BIOMAZE,                         -1,
1127     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1128     &li.biomaze[2],                     3
1129   },
1130   {
1131     EL_BIOMAZE,                         -1,
1132     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1133     &li.biomaze[3],                     3
1134   },
1135
1136   {
1137     EL_TIMEGATE_SWITCH,                 -1,
1138     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1139     &li.time_timegate,                  10
1140   },
1141
1142   {
1143     EL_LIGHT_SWITCH_ACTIVE,             -1,
1144     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1145     &li.time_light,                     10
1146   },
1147
1148   {
1149     EL_SHIELD_NORMAL,                   -1,
1150     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1151     &li.shield_normal_time,             10
1152   },
1153   {
1154     EL_SHIELD_NORMAL,                   -1,
1155     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1156     &li.score[SC_SHIELD],               10
1157   },
1158
1159   {
1160     EL_SHIELD_DEADLY,                   -1,
1161     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1162     &li.shield_deadly_time,             10
1163   },
1164   {
1165     EL_SHIELD_DEADLY,                   -1,
1166     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1167     &li.score[SC_SHIELD],               10
1168   },
1169
1170   {
1171     EL_EXTRA_TIME,                      -1,
1172     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1173     &li.extra_time,                     10
1174   },
1175   {
1176     EL_EXTRA_TIME,                      -1,
1177     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1178     &li.extra_time_score,               10
1179   },
1180
1181   {
1182     EL_TIME_ORB_FULL,                   -1,
1183     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1184     &li.time_orb_time,                  10
1185   },
1186   {
1187     EL_TIME_ORB_FULL,                   -1,
1188     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1189     &li.use_time_orb_bug,               FALSE
1190   },
1191
1192   {
1193     EL_SPRING,                          -1,
1194     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1195     &li.use_spring_bug,                 FALSE
1196   },
1197
1198   {
1199     EL_EMC_ANDROID,                     -1,
1200     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1201     &li.android_move_time,              10
1202   },
1203   {
1204     EL_EMC_ANDROID,                     -1,
1205     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1206     &li.android_clone_time,             10
1207   },
1208   {
1209     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1210     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1211     &li.android_clone_element[0],       EL_EMPTY, NULL,
1212     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1213   },
1214   {
1215     EL_EMC_ANDROID,                     -1,
1216     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1217     &li.android_clone_element[0],       EL_EMPTY, NULL,
1218     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1219   },
1220
1221   {
1222     EL_EMC_LENSES,                      -1,
1223     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1224     &li.lenses_score,                   10
1225   },
1226   {
1227     EL_EMC_LENSES,                      -1,
1228     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1229     &li.lenses_time,                    10
1230   },
1231
1232   {
1233     EL_EMC_MAGNIFIER,                   -1,
1234     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1235     &li.magnify_score,                  10
1236   },
1237   {
1238     EL_EMC_MAGNIFIER,                   -1,
1239     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1240     &li.magnify_time,                   10
1241   },
1242
1243   {
1244     EL_EMC_MAGIC_BALL,                  -1,
1245     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1246     &li.ball_time,                      10
1247   },
1248   {
1249     EL_EMC_MAGIC_BALL,                  -1,
1250     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1251     &li.ball_random,                    FALSE
1252   },
1253   {
1254     EL_EMC_MAGIC_BALL,                  -1,
1255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1256     &li.ball_active_initial,            FALSE
1257   },
1258   {
1259     EL_EMC_MAGIC_BALL,                  -1,
1260     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1261     &li.ball_content,                   EL_EMPTY, NULL,
1262     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1263   },
1264
1265   {
1266     EL_SOKOBAN_FIELD_EMPTY,             -1,
1267     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1268     &li.sb_fields_needed,               TRUE
1269   },
1270
1271   {
1272     EL_SOKOBAN_OBJECT,                  -1,
1273     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1274     &li.sb_objects_needed,              TRUE
1275   },
1276
1277   {
1278     EL_MM_MCDUFFIN,                     -1,
1279     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1280     &li.mm_laser_red,                   FALSE
1281   },
1282   {
1283     EL_MM_MCDUFFIN,                     -1,
1284     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1285     &li.mm_laser_green,                 FALSE
1286   },
1287   {
1288     EL_MM_MCDUFFIN,                     -1,
1289     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1290     &li.mm_laser_blue,                  TRUE
1291   },
1292
1293   {
1294     EL_DF_LASER,                        -1,
1295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1296     &li.df_laser_red,                   TRUE
1297   },
1298   {
1299     EL_DF_LASER,                        -1,
1300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1301     &li.df_laser_green,                 TRUE
1302   },
1303   {
1304     EL_DF_LASER,                        -1,
1305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1306     &li.df_laser_blue,                  FALSE
1307   },
1308
1309   {
1310     EL_MM_FUSE_ACTIVE,                  -1,
1311     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1312     &li.mm_time_fuse,                   25
1313   },
1314   {
1315     EL_MM_BOMB,                         -1,
1316     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1317     &li.mm_time_bomb,                   75
1318   },
1319
1320   {
1321     EL_MM_GRAY_BALL,                    -1,
1322     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1323     &li.mm_time_ball,                   75
1324   },
1325   {
1326     EL_MM_GRAY_BALL,                    -1,
1327     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1328     &li.mm_ball_choice_mode,            ANIM_RANDOM
1329   },
1330   {
1331     EL_MM_GRAY_BALL,                    -1,
1332     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1333     &li.mm_ball_content,                EL_EMPTY, NULL,
1334     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1335   },
1336   {
1337     EL_MM_GRAY_BALL,                    -1,
1338     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1339     &li.rotate_mm_ball_content,         TRUE
1340   },
1341   {
1342     EL_MM_GRAY_BALL,                    -1,
1343     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1344     &li.explode_mm_ball,                FALSE
1345   },
1346
1347   {
1348     EL_MM_STEEL_BLOCK,                  -1,
1349     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1350     &li.mm_time_block,                  75
1351   },
1352   {
1353     EL_MM_LIGHTBALL,                    -1,
1354     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1355     &li.score[SC_ELEM_BONUS],           10
1356   },
1357
1358   {
1359     -1,                                 -1,
1360     -1,                                 -1,
1361     NULL,                               -1
1362   }
1363 };
1364
1365 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1366 {
1367   {
1368     -1,                                 -1,
1369     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1370     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1371   },
1372   {
1373     -1,                                 -1,
1374     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1375     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1376   },
1377
1378   {
1379     -1,                                 -1,
1380     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1381     &xx_envelope.autowrap,              FALSE
1382   },
1383   {
1384     -1,                                 -1,
1385     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1386     &xx_envelope.centered,              FALSE
1387   },
1388
1389   {
1390     -1,                                 -1,
1391     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1392     &xx_envelope.text,                  -1, NULL,
1393     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1394     &xx_default_string_empty[0]
1395   },
1396
1397   {
1398     -1,                                 -1,
1399     -1,                                 -1,
1400     NULL,                               -1
1401   }
1402 };
1403
1404 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1405 {
1406   {
1407     -1,                                 -1,
1408     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1409     &xx_ei.description[0],              -1,
1410     &yy_ei.description[0],
1411     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1412     &xx_default_description[0]
1413   },
1414
1415   {
1416     -1,                                 -1,
1417     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1418     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1419     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1420   },
1421 #if ENABLE_RESERVED_CODE
1422   // (reserved for later use)
1423   {
1424     -1,                                 -1,
1425     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1426     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1427     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1428   },
1429 #endif
1430
1431   {
1432     -1,                                 -1,
1433     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1434     &xx_ei.use_gfx_element,             FALSE,
1435     &yy_ei.use_gfx_element
1436   },
1437   {
1438     -1,                                 -1,
1439     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1440     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1441     &yy_ei.gfx_element_initial
1442   },
1443
1444   {
1445     -1,                                 -1,
1446     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1447     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1448     &yy_ei.access_direction
1449   },
1450
1451   {
1452     -1,                                 -1,
1453     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1454     &xx_ei.collect_score_initial,       10,
1455     &yy_ei.collect_score_initial
1456   },
1457   {
1458     -1,                                 -1,
1459     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1460     &xx_ei.collect_count_initial,       1,
1461     &yy_ei.collect_count_initial
1462   },
1463
1464   {
1465     -1,                                 -1,
1466     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1467     &xx_ei.ce_value_fixed_initial,      0,
1468     &yy_ei.ce_value_fixed_initial
1469   },
1470   {
1471     -1,                                 -1,
1472     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1473     &xx_ei.ce_value_random_initial,     0,
1474     &yy_ei.ce_value_random_initial
1475   },
1476   {
1477     -1,                                 -1,
1478     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1479     &xx_ei.use_last_ce_value,           FALSE,
1480     &yy_ei.use_last_ce_value
1481   },
1482
1483   {
1484     -1,                                 -1,
1485     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1486     &xx_ei.push_delay_fixed,            8,
1487     &yy_ei.push_delay_fixed
1488   },
1489   {
1490     -1,                                 -1,
1491     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1492     &xx_ei.push_delay_random,           8,
1493     &yy_ei.push_delay_random
1494   },
1495   {
1496     -1,                                 -1,
1497     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1498     &xx_ei.drop_delay_fixed,            0,
1499     &yy_ei.drop_delay_fixed
1500   },
1501   {
1502     -1,                                 -1,
1503     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1504     &xx_ei.drop_delay_random,           0,
1505     &yy_ei.drop_delay_random
1506   },
1507   {
1508     -1,                                 -1,
1509     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1510     &xx_ei.move_delay_fixed,            0,
1511     &yy_ei.move_delay_fixed
1512   },
1513   {
1514     -1,                                 -1,
1515     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1516     &xx_ei.move_delay_random,           0,
1517     &yy_ei.move_delay_random
1518   },
1519   {
1520     -1,                                 -1,
1521     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1522     &xx_ei.step_delay_fixed,            0,
1523     &yy_ei.step_delay_fixed
1524   },
1525   {
1526     -1,                                 -1,
1527     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1528     &xx_ei.step_delay_random,           0,
1529     &yy_ei.step_delay_random
1530   },
1531
1532   {
1533     -1,                                 -1,
1534     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1535     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1536     &yy_ei.move_pattern
1537   },
1538   {
1539     -1,                                 -1,
1540     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1541     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1542     &yy_ei.move_direction_initial
1543   },
1544   {
1545     -1,                                 -1,
1546     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1547     &xx_ei.move_stepsize,               TILEX / 8,
1548     &yy_ei.move_stepsize
1549   },
1550
1551   {
1552     -1,                                 -1,
1553     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1554     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1555     &yy_ei.move_enter_element
1556   },
1557   {
1558     -1,                                 -1,
1559     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1560     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1561     &yy_ei.move_leave_element
1562   },
1563   {
1564     -1,                                 -1,
1565     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1566     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1567     &yy_ei.move_leave_type
1568   },
1569
1570   {
1571     -1,                                 -1,
1572     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1573     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1574     &yy_ei.slippery_type
1575   },
1576
1577   {
1578     -1,                                 -1,
1579     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1580     &xx_ei.explosion_type,              EXPLODES_3X3,
1581     &yy_ei.explosion_type
1582   },
1583   {
1584     -1,                                 -1,
1585     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1586     &xx_ei.explosion_delay,             16,
1587     &yy_ei.explosion_delay
1588   },
1589   {
1590     -1,                                 -1,
1591     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1592     &xx_ei.ignition_delay,              8,
1593     &yy_ei.ignition_delay
1594   },
1595
1596   {
1597     -1,                                 -1,
1598     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1599     &xx_ei.content,                     EL_EMPTY_SPACE,
1600     &yy_ei.content,
1601     &xx_num_contents,                   1, 1
1602   },
1603
1604   // ---------- "num_change_pages" must be the last entry ---------------------
1605
1606   {
1607     -1,                                 SAVE_CONF_ALWAYS,
1608     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1609     &xx_ei.num_change_pages,            1,
1610     &yy_ei.num_change_pages
1611   },
1612
1613   {
1614     -1,                                 -1,
1615     -1,                                 -1,
1616     NULL,                               -1,
1617     NULL
1618   }
1619 };
1620
1621 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1622 {
1623   // ---------- "current_change_page" must be the first entry -----------------
1624
1625   {
1626     -1,                                 SAVE_CONF_ALWAYS,
1627     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1628     &xx_current_change_page,            -1
1629   },
1630
1631   // ---------- (the remaining entries can be in any order) -------------------
1632
1633   {
1634     -1,                                 -1,
1635     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1636     &xx_change.can_change,              FALSE
1637   },
1638
1639   {
1640     -1,                                 -1,
1641     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1642     &xx_event_bits[0],                  0
1643   },
1644   {
1645     -1,                                 -1,
1646     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1647     &xx_event_bits[1],                  0
1648   },
1649
1650   {
1651     -1,                                 -1,
1652     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1653     &xx_change.trigger_player,          CH_PLAYER_ANY
1654   },
1655   {
1656     -1,                                 -1,
1657     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1658     &xx_change.trigger_side,            CH_SIDE_ANY
1659   },
1660   {
1661     -1,                                 -1,
1662     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1663     &xx_change.trigger_page,            CH_PAGE_ANY
1664   },
1665
1666   {
1667     -1,                                 -1,
1668     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1669     &xx_change.target_element,          EL_EMPTY_SPACE
1670   },
1671
1672   {
1673     -1,                                 -1,
1674     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1675     &xx_change.delay_fixed,             0
1676   },
1677   {
1678     -1,                                 -1,
1679     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1680     &xx_change.delay_random,            0
1681   },
1682   {
1683     -1,                                 -1,
1684     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1685     &xx_change.delay_frames,            FRAMES_PER_SECOND
1686   },
1687
1688   {
1689     -1,                                 -1,
1690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1691     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1692   },
1693
1694   {
1695     -1,                                 -1,
1696     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1697     &xx_change.explode,                 FALSE
1698   },
1699   {
1700     -1,                                 -1,
1701     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1702     &xx_change.use_target_content,      FALSE
1703   },
1704   {
1705     -1,                                 -1,
1706     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1707     &xx_change.only_if_complete,        FALSE
1708   },
1709   {
1710     -1,                                 -1,
1711     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1712     &xx_change.use_random_replace,      FALSE
1713   },
1714   {
1715     -1,                                 -1,
1716     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1717     &xx_change.random_percentage,       100
1718   },
1719   {
1720     -1,                                 -1,
1721     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1722     &xx_change.replace_when,            CP_WHEN_EMPTY
1723   },
1724
1725   {
1726     -1,                                 -1,
1727     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1728     &xx_change.has_action,              FALSE
1729   },
1730   {
1731     -1,                                 -1,
1732     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1733     &xx_change.action_type,             CA_NO_ACTION
1734   },
1735   {
1736     -1,                                 -1,
1737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1738     &xx_change.action_mode,             CA_MODE_UNDEFINED
1739   },
1740   {
1741     -1,                                 -1,
1742     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1743     &xx_change.action_arg,              CA_ARG_UNDEFINED
1744   },
1745
1746   {
1747     -1,                                 -1,
1748     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1749     &xx_change.action_element,          EL_EMPTY_SPACE
1750   },
1751
1752   {
1753     -1,                                 -1,
1754     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1755     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1756     &xx_num_contents,                   1, 1
1757   },
1758
1759   {
1760     -1,                                 -1,
1761     -1,                                 -1,
1762     NULL,                               -1
1763   }
1764 };
1765
1766 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1767 {
1768   {
1769     -1,                                 -1,
1770     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1771     &xx_ei.description[0],              -1, NULL,
1772     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1773     &xx_default_description[0]
1774   },
1775
1776   {
1777     -1,                                 -1,
1778     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1779     &xx_ei.use_gfx_element,             FALSE
1780   },
1781   {
1782     -1,                                 -1,
1783     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1784     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1785   },
1786
1787   {
1788     -1,                                 -1,
1789     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1790     &xx_group.choice_mode,              ANIM_RANDOM
1791   },
1792
1793   {
1794     -1,                                 -1,
1795     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1796     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1797     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1798   },
1799
1800   {
1801     -1,                                 -1,
1802     -1,                                 -1,
1803     NULL,                               -1
1804   }
1805 };
1806
1807 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1808 {
1809   {
1810     -1,                                 -1,
1811     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1812     &xx_ei.use_gfx_element,             FALSE
1813   },
1814   {
1815     -1,                                 -1,
1816     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1817     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1818   },
1819
1820   {
1821     -1,                                 -1,
1822     -1,                                 -1,
1823     NULL,                               -1
1824   }
1825 };
1826
1827 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1828 {
1829   {
1830     EL_PLAYER_1,                        -1,
1831     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1832     &li.block_snap_field,               TRUE
1833   },
1834   {
1835     EL_PLAYER_1,                        -1,
1836     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1837     &li.continuous_snapping,            TRUE
1838   },
1839   {
1840     EL_PLAYER_1,                        -1,
1841     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1842     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1843   },
1844   {
1845     EL_PLAYER_1,                        -1,
1846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1847     &li.use_start_element[0],           FALSE
1848   },
1849   {
1850     EL_PLAYER_1,                        -1,
1851     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1852     &li.start_element[0],               EL_PLAYER_1
1853   },
1854   {
1855     EL_PLAYER_1,                        -1,
1856     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1857     &li.use_artwork_element[0],         FALSE
1858   },
1859   {
1860     EL_PLAYER_1,                        -1,
1861     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1862     &li.artwork_element[0],             EL_PLAYER_1
1863   },
1864   {
1865     EL_PLAYER_1,                        -1,
1866     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1867     &li.use_explosion_element[0],       FALSE
1868   },
1869   {
1870     EL_PLAYER_1,                        -1,
1871     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1872     &li.explosion_element[0],           EL_PLAYER_1
1873   },
1874
1875   {
1876     -1,                                 -1,
1877     -1,                                 -1,
1878     NULL,                               -1
1879   }
1880 };
1881
1882 static struct
1883 {
1884   int filetype;
1885   char *id;
1886 }
1887 filetype_id_list[] =
1888 {
1889   { LEVEL_FILE_TYPE_RND,        "RND"   },
1890   { LEVEL_FILE_TYPE_BD,         "BD"    },
1891   { LEVEL_FILE_TYPE_EM,         "EM"    },
1892   { LEVEL_FILE_TYPE_SP,         "SP"    },
1893   { LEVEL_FILE_TYPE_DX,         "DX"    },
1894   { LEVEL_FILE_TYPE_SB,         "SB"    },
1895   { LEVEL_FILE_TYPE_DC,         "DC"    },
1896   { LEVEL_FILE_TYPE_MM,         "MM"    },
1897   { LEVEL_FILE_TYPE_MM,         "DF"    },
1898   { -1,                         NULL    },
1899 };
1900
1901
1902 // ============================================================================
1903 // level file functions
1904 // ============================================================================
1905
1906 static boolean check_special_flags(char *flag)
1907 {
1908   if (strEqual(options.special_flags, flag) ||
1909       strEqual(leveldir_current->special_flags, flag))
1910     return TRUE;
1911
1912   return FALSE;
1913 }
1914
1915 static struct DateInfo getCurrentDate(void)
1916 {
1917   time_t epoch_seconds = time(NULL);
1918   struct tm *now = localtime(&epoch_seconds);
1919   struct DateInfo date;
1920
1921   date.year  = now->tm_year + 1900;
1922   date.month = now->tm_mon  + 1;
1923   date.day   = now->tm_mday;
1924
1925   date.src   = DATE_SRC_CLOCK;
1926
1927   return date;
1928 }
1929
1930 static void resetEventFlags(struct ElementChangeInfo *change)
1931 {
1932   int i;
1933
1934   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1935     change->has_event[i] = FALSE;
1936 }
1937
1938 static void resetEventBits(void)
1939 {
1940   int i;
1941
1942   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1943     xx_event_bits[i] = 0;
1944 }
1945
1946 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1947 {
1948   int i;
1949
1950   /* important: only change event flag if corresponding event bit is set
1951      (this is because all xx_event_bits[] values are loaded separately,
1952      and all xx_event_bits[] values are set back to zero before loading
1953      another value xx_event_bits[x] (each value representing 32 flags)) */
1954
1955   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1956     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1957       change->has_event[i] = TRUE;
1958 }
1959
1960 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1961 {
1962   int i;
1963
1964   /* in contrast to the above function setEventFlagsFromEventBits(), it
1965      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1966      depending on the corresponding change->has_event[i] values here, as
1967      all xx_event_bits[] values are reset in resetEventBits() before */
1968
1969   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1970     if (change->has_event[i])
1971       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1972 }
1973
1974 static char *getDefaultElementDescription(struct ElementInfo *ei)
1975 {
1976   static char description[MAX_ELEMENT_NAME_LEN + 1];
1977   char *default_description = (ei->custom_description != NULL ?
1978                                ei->custom_description :
1979                                ei->editor_description);
1980   int i;
1981
1982   // always start with reliable default values
1983   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1984     description[i] = '\0';
1985
1986   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
1987   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
1988
1989   return &description[0];
1990 }
1991
1992 static void setElementDescriptionToDefault(struct ElementInfo *ei)
1993 {
1994   char *default_description = getDefaultElementDescription(ei);
1995   int i;
1996
1997   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1998     ei->description[i] = default_description[i];
1999 }
2000
2001 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2002 {
2003   int i;
2004
2005   for (i = 0; conf[i].data_type != -1; i++)
2006   {
2007     int default_value = conf[i].default_value;
2008     int data_type = conf[i].data_type;
2009     int conf_type = conf[i].conf_type;
2010     int byte_mask = conf_type & CONF_MASK_BYTES;
2011
2012     if (byte_mask == CONF_MASK_MULTI_BYTES)
2013     {
2014       int default_num_entities = conf[i].default_num_entities;
2015       int max_num_entities = conf[i].max_num_entities;
2016
2017       *(int *)(conf[i].num_entities) = default_num_entities;
2018
2019       if (data_type == TYPE_STRING)
2020       {
2021         char *default_string = conf[i].default_string;
2022         char *string = (char *)(conf[i].value);
2023
2024         strncpy(string, default_string, max_num_entities);
2025       }
2026       else if (data_type == TYPE_ELEMENT_LIST)
2027       {
2028         int *element_array = (int *)(conf[i].value);
2029         int j;
2030
2031         for (j = 0; j < max_num_entities; j++)
2032           element_array[j] = default_value;
2033       }
2034       else if (data_type == TYPE_CONTENT_LIST)
2035       {
2036         struct Content *content = (struct Content *)(conf[i].value);
2037         int c, x, y;
2038
2039         for (c = 0; c < max_num_entities; c++)
2040           for (y = 0; y < 3; y++)
2041             for (x = 0; x < 3; x++)
2042               content[c].e[x][y] = default_value;
2043       }
2044     }
2045     else        // constant size configuration data (1, 2 or 4 bytes)
2046     {
2047       if (data_type == TYPE_BOOLEAN)
2048         *(boolean *)(conf[i].value) = default_value;
2049       else
2050         *(int *)    (conf[i].value) = default_value;
2051     }
2052   }
2053 }
2054
2055 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2056 {
2057   int i;
2058
2059   for (i = 0; conf[i].data_type != -1; i++)
2060   {
2061     int data_type = conf[i].data_type;
2062     int conf_type = conf[i].conf_type;
2063     int byte_mask = conf_type & CONF_MASK_BYTES;
2064
2065     if (byte_mask == CONF_MASK_MULTI_BYTES)
2066     {
2067       int max_num_entities = conf[i].max_num_entities;
2068
2069       if (data_type == TYPE_STRING)
2070       {
2071         char *string      = (char *)(conf[i].value);
2072         char *string_copy = (char *)(conf[i].value_copy);
2073
2074         strncpy(string_copy, string, max_num_entities);
2075       }
2076       else if (data_type == TYPE_ELEMENT_LIST)
2077       {
2078         int *element_array      = (int *)(conf[i].value);
2079         int *element_array_copy = (int *)(conf[i].value_copy);
2080         int j;
2081
2082         for (j = 0; j < max_num_entities; j++)
2083           element_array_copy[j] = element_array[j];
2084       }
2085       else if (data_type == TYPE_CONTENT_LIST)
2086       {
2087         struct Content *content      = (struct Content *)(conf[i].value);
2088         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2089         int c, x, y;
2090
2091         for (c = 0; c < max_num_entities; c++)
2092           for (y = 0; y < 3; y++)
2093             for (x = 0; x < 3; x++)
2094               content_copy[c].e[x][y] = content[c].e[x][y];
2095       }
2096     }
2097     else        // constant size configuration data (1, 2 or 4 bytes)
2098     {
2099       if (data_type == TYPE_BOOLEAN)
2100         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2101       else
2102         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2103     }
2104   }
2105 }
2106
2107 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2108 {
2109   int i;
2110
2111   xx_ei = *ei_from;     // copy element data into temporary buffer
2112   yy_ei = *ei_to;       // copy element data into temporary buffer
2113
2114   copyConfigFromConfigList(chunk_config_CUSX_base);
2115
2116   *ei_from = xx_ei;
2117   *ei_to   = yy_ei;
2118
2119   // ---------- reinitialize and copy change pages ----------
2120
2121   ei_to->num_change_pages = ei_from->num_change_pages;
2122   ei_to->current_change_page = ei_from->current_change_page;
2123
2124   setElementChangePages(ei_to, ei_to->num_change_pages);
2125
2126   for (i = 0; i < ei_to->num_change_pages; i++)
2127     ei_to->change_page[i] = ei_from->change_page[i];
2128
2129   // ---------- copy group element info ----------
2130   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2131     *ei_to->group = *ei_from->group;
2132
2133   // mark this custom element as modified
2134   ei_to->modified_settings = TRUE;
2135 }
2136
2137 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2138 {
2139   int change_page_size = sizeof(struct ElementChangeInfo);
2140
2141   ei->num_change_pages = MAX(1, change_pages);
2142
2143   ei->change_page =
2144     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2145
2146   if (ei->current_change_page >= ei->num_change_pages)
2147     ei->current_change_page = ei->num_change_pages - 1;
2148
2149   ei->change = &ei->change_page[ei->current_change_page];
2150 }
2151
2152 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2153 {
2154   xx_change = *change;          // copy change data into temporary buffer
2155
2156   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2157
2158   *change = xx_change;
2159
2160   resetEventFlags(change);
2161
2162   change->direct_action = 0;
2163   change->other_action = 0;
2164
2165   change->pre_change_function = NULL;
2166   change->change_function = NULL;
2167   change->post_change_function = NULL;
2168 }
2169
2170 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2171 {
2172   int i, x, y;
2173
2174   li = *level;          // copy level data into temporary buffer
2175   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2176   *level = li;          // copy temporary buffer back to level data
2177
2178   setLevelInfoToDefaults_BD();
2179   setLevelInfoToDefaults_EM();
2180   setLevelInfoToDefaults_SP();
2181   setLevelInfoToDefaults_MM();
2182
2183   level->native_bd_level = &native_bd_level;
2184   level->native_em_level = &native_em_level;
2185   level->native_sp_level = &native_sp_level;
2186   level->native_mm_level = &native_mm_level;
2187
2188   level->file_version = FILE_VERSION_ACTUAL;
2189   level->game_version = GAME_VERSION_ACTUAL;
2190
2191   level->creation_date = getCurrentDate();
2192
2193   level->encoding_16bit_field  = TRUE;
2194   level->encoding_16bit_yamyam = TRUE;
2195   level->encoding_16bit_amoeba = TRUE;
2196
2197   // clear level name and level author string buffers
2198   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2199     level->name[i] = '\0';
2200   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2201     level->author[i] = '\0';
2202
2203   // set level name and level author to default values
2204   strcpy(level->name, NAMELESS_LEVEL_NAME);
2205   strcpy(level->author, ANONYMOUS_NAME);
2206
2207   // set level playfield to playable default level with player and exit
2208   for (x = 0; x < MAX_LEV_FIELDX; x++)
2209     for (y = 0; y < MAX_LEV_FIELDY; y++)
2210       level->field[x][y] = EL_SAND;
2211
2212   level->field[0][0] = EL_PLAYER_1;
2213   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2214
2215   BorderElement = EL_STEELWALL;
2216
2217   // detect custom elements when loading them
2218   level->file_has_custom_elements = FALSE;
2219
2220   // set all bug compatibility flags to "false" => do not emulate this bug
2221   level->use_action_after_change_bug = FALSE;
2222
2223   if (leveldir_current)
2224   {
2225     // try to determine better author name than 'anonymous'
2226     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2227     {
2228       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2229       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2230     }
2231     else
2232     {
2233       switch (LEVELCLASS(leveldir_current))
2234       {
2235         case LEVELCLASS_TUTORIAL:
2236           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2237           break;
2238
2239         case LEVELCLASS_CONTRIB:
2240           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2241           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2242           break;
2243
2244         case LEVELCLASS_PRIVATE:
2245           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2246           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2247           break;
2248
2249         default:
2250           // keep default value
2251           break;
2252       }
2253     }
2254   }
2255 }
2256
2257 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2258 {
2259   static boolean clipboard_elements_initialized = FALSE;
2260   int i;
2261
2262   InitElementPropertiesStatic();
2263
2264   li = *level;          // copy level data into temporary buffer
2265   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2266   *level = li;          // copy temporary buffer back to level data
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269   {
2270     int element = i;
2271     struct ElementInfo *ei = &element_info[element];
2272
2273     if (element == EL_MM_GRAY_BALL)
2274     {
2275       struct LevelInfo_MM *level_mm = level->native_mm_level;
2276       int j;
2277
2278       for (j = 0; j < level->num_mm_ball_contents; j++)
2279         level->mm_ball_content[j] =
2280           map_element_MM_to_RND(level_mm->ball_content[j]);
2281     }
2282
2283     // never initialize clipboard elements after the very first time
2284     // (to be able to use clipboard elements between several levels)
2285     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2286       continue;
2287
2288     if (IS_ENVELOPE(element))
2289     {
2290       int envelope_nr = element - EL_ENVELOPE_1;
2291
2292       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2293
2294       level->envelope[envelope_nr] = xx_envelope;
2295     }
2296
2297     if (IS_CUSTOM_ELEMENT(element) ||
2298         IS_GROUP_ELEMENT(element) ||
2299         IS_INTERNAL_ELEMENT(element))
2300     {
2301       xx_ei = *ei;      // copy element data into temporary buffer
2302
2303       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2304
2305       *ei = xx_ei;
2306     }
2307
2308     setElementChangePages(ei, 1);
2309     setElementChangeInfoToDefaults(ei->change);
2310
2311     if (IS_CUSTOM_ELEMENT(element) ||
2312         IS_GROUP_ELEMENT(element))
2313     {
2314       setElementDescriptionToDefault(ei);
2315
2316       ei->modified_settings = FALSE;
2317     }
2318
2319     if (IS_CUSTOM_ELEMENT(element) ||
2320         IS_INTERNAL_ELEMENT(element))
2321     {
2322       // internal values used in level editor
2323
2324       ei->access_type = 0;
2325       ei->access_layer = 0;
2326       ei->access_protected = 0;
2327       ei->walk_to_action = 0;
2328       ei->smash_targets = 0;
2329       ei->deadliness = 0;
2330
2331       ei->can_explode_by_fire = FALSE;
2332       ei->can_explode_smashed = FALSE;
2333       ei->can_explode_impact = FALSE;
2334
2335       ei->current_change_page = 0;
2336     }
2337
2338     if (IS_GROUP_ELEMENT(element) ||
2339         IS_INTERNAL_ELEMENT(element))
2340     {
2341       struct ElementGroupInfo *group;
2342
2343       // initialize memory for list of elements in group
2344       if (ei->group == NULL)
2345         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2346
2347       group = ei->group;
2348
2349       xx_group = *group;        // copy group data into temporary buffer
2350
2351       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2352
2353       *group = xx_group;
2354     }
2355
2356     if (IS_EMPTY_ELEMENT(element) ||
2357         IS_INTERNAL_ELEMENT(element))
2358     {
2359       xx_ei = *ei;              // copy element data into temporary buffer
2360
2361       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2362
2363       *ei = xx_ei;
2364     }
2365   }
2366
2367   clipboard_elements_initialized = TRUE;
2368 }
2369
2370 static void setLevelInfoToDefaults(struct LevelInfo *level,
2371                                    boolean level_info_only,
2372                                    boolean reset_file_status)
2373 {
2374   setLevelInfoToDefaults_Level(level);
2375
2376   if (!level_info_only)
2377     setLevelInfoToDefaults_Elements(level);
2378
2379   if (reset_file_status)
2380   {
2381     level->no_valid_file = FALSE;
2382     level->no_level_file = FALSE;
2383   }
2384
2385   level->changed = FALSE;
2386 }
2387
2388 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2389 {
2390   level_file_info->nr = 0;
2391   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2392   level_file_info->packed = FALSE;
2393
2394   setString(&level_file_info->basename, NULL);
2395   setString(&level_file_info->filename, NULL);
2396 }
2397
2398 int getMappedElement_SB(int, boolean);
2399
2400 static void ActivateLevelTemplate(void)
2401 {
2402   int x, y;
2403
2404   if (check_special_flags("load_xsb_to_ces"))
2405   {
2406     // fill smaller playfields with padding "beyond border wall" elements
2407     if (level.fieldx < level_template.fieldx ||
2408         level.fieldy < level_template.fieldy)
2409     {
2410       short field[level.fieldx][level.fieldy];
2411       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2412       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2413       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2414       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2415
2416       // copy old playfield (which is smaller than the visible area)
2417       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2418         field[x][y] = level.field[x][y];
2419
2420       // fill new, larger playfield with "beyond border wall" elements
2421       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2422         level.field[x][y] = getMappedElement_SB('_', TRUE);
2423
2424       // copy the old playfield to the middle of the new playfield
2425       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2426         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2427
2428       level.fieldx = new_fieldx;
2429       level.fieldy = new_fieldy;
2430     }
2431   }
2432
2433   // Currently there is no special action needed to activate the template
2434   // data, because 'element_info' property settings overwrite the original
2435   // level data, while all other variables do not change.
2436
2437   // Exception: 'from_level_template' elements in the original level playfield
2438   // are overwritten with the corresponding elements at the same position in
2439   // playfield from the level template.
2440
2441   for (x = 0; x < level.fieldx; x++)
2442     for (y = 0; y < level.fieldy; y++)
2443       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2444         level.field[x][y] = level_template.field[x][y];
2445
2446   if (check_special_flags("load_xsb_to_ces"))
2447   {
2448     struct LevelInfo level_backup = level;
2449
2450     // overwrite all individual level settings from template level settings
2451     level = level_template;
2452
2453     // restore level file info
2454     level.file_info = level_backup.file_info;
2455
2456     // restore playfield size
2457     level.fieldx = level_backup.fieldx;
2458     level.fieldy = level_backup.fieldy;
2459
2460     // restore playfield content
2461     for (x = 0; x < level.fieldx; x++)
2462       for (y = 0; y < level.fieldy; y++)
2463         level.field[x][y] = level_backup.field[x][y];
2464
2465     // restore name and author from individual level
2466     strcpy(level.name,   level_backup.name);
2467     strcpy(level.author, level_backup.author);
2468
2469     // restore flag "use_custom_template"
2470     level.use_custom_template = level_backup.use_custom_template;
2471   }
2472 }
2473
2474 static boolean checkForPackageFromBasename_BD(char *basename)
2475 {
2476   // check for native BD level file extensions
2477   if (!strSuffixLower(basename, ".bd") &&
2478       !strSuffixLower(basename, ".bdr") &&
2479       !strSuffixLower(basename, ".brc") &&
2480       !strSuffixLower(basename, ".gds"))
2481     return FALSE;
2482
2483   // check for standard single-level BD files (like "001.bd")
2484   if (strSuffixLower(basename, ".bd") &&
2485       strlen(basename) == 6 &&
2486       basename[0] >= '0' && basename[0] <= '9' &&
2487       basename[1] >= '0' && basename[1] <= '9' &&
2488       basename[2] >= '0' && basename[2] <= '9')
2489     return FALSE;
2490
2491   // this is a level package in native BD file format
2492   return TRUE;
2493 }
2494
2495 static char *getLevelFilenameFromBasename(char *basename)
2496 {
2497   static char *filename = NULL;
2498
2499   checked_free(filename);
2500
2501   filename = getPath2(getCurrentLevelDir(), basename);
2502
2503   return filename;
2504 }
2505
2506 static int getFileTypeFromBasename(char *basename)
2507 {
2508   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2509
2510   static char *filename = NULL;
2511   struct stat file_status;
2512
2513   // ---------- try to determine file type from filename ----------
2514
2515   // check for typical filename of a Supaplex level package file
2516   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2517     return LEVEL_FILE_TYPE_SP;
2518
2519   // check for typical filename of a Diamond Caves II level package file
2520   if (strSuffixLower(basename, ".dc") ||
2521       strSuffixLower(basename, ".dc2"))
2522     return LEVEL_FILE_TYPE_DC;
2523
2524   // check for typical filename of a Sokoban level package file
2525   if (strSuffixLower(basename, ".xsb") &&
2526       strchr(basename, '%') == NULL)
2527     return LEVEL_FILE_TYPE_SB;
2528
2529   // check for typical filename of a Boulder Dash (GDash) level package file
2530   if (checkForPackageFromBasename_BD(basename))
2531     return LEVEL_FILE_TYPE_BD;
2532
2533   // ---------- try to determine file type from filesize ----------
2534
2535   checked_free(filename);
2536   filename = getPath2(getCurrentLevelDir(), basename);
2537
2538   if (stat(filename, &file_status) == 0)
2539   {
2540     // check for typical filesize of a Supaplex level package file
2541     if (file_status.st_size == 170496)
2542       return LEVEL_FILE_TYPE_SP;
2543   }
2544
2545   return LEVEL_FILE_TYPE_UNKNOWN;
2546 }
2547
2548 static int getFileTypeFromMagicBytes(char *filename, int type)
2549 {
2550   File *file;
2551
2552   if ((file = openFile(filename, MODE_READ)))
2553   {
2554     char chunk_name[CHUNK_ID_LEN + 1];
2555
2556     getFileChunkBE(file, chunk_name, NULL);
2557
2558     if (strEqual(chunk_name, "MMII") ||
2559         strEqual(chunk_name, "MIRR"))
2560       type = LEVEL_FILE_TYPE_MM;
2561
2562     closeFile(file);
2563   }
2564
2565   return type;
2566 }
2567
2568 static boolean checkForPackageFromBasename(char *basename)
2569 {
2570   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2571   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2572
2573   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2574 }
2575
2576 static char *getSingleLevelBasenameExt(int nr, char *extension)
2577 {
2578   static char basename[MAX_FILENAME_LEN];
2579
2580   if (nr < 0)
2581     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2582   else
2583     sprintf(basename, "%03d.%s", nr, extension);
2584
2585   return basename;
2586 }
2587
2588 static char *getSingleLevelBasename(int nr)
2589 {
2590   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2591 }
2592
2593 static char *getPackedLevelBasename(int type)
2594 {
2595   static char basename[MAX_FILENAME_LEN];
2596   char *directory = getCurrentLevelDir();
2597   Directory *dir;
2598   DirectoryEntry *dir_entry;
2599
2600   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2601
2602   if ((dir = openDirectory(directory)) == NULL)
2603   {
2604     Warn("cannot read current level directory '%s'", directory);
2605
2606     return basename;
2607   }
2608
2609   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2610   {
2611     char *entry_basename = dir_entry->basename;
2612     int entry_type = getFileTypeFromBasename(entry_basename);
2613
2614     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2615     {
2616       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2617           type == entry_type)
2618       {
2619         strcpy(basename, entry_basename);
2620
2621         break;
2622       }
2623     }
2624   }
2625
2626   closeDirectory(dir);
2627
2628   return basename;
2629 }
2630
2631 static char *getSingleLevelFilename(int nr)
2632 {
2633   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2634 }
2635
2636 #if ENABLE_UNUSED_CODE
2637 static char *getPackedLevelFilename(int type)
2638 {
2639   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2640 }
2641 #endif
2642
2643 char *getDefaultLevelFilename(int nr)
2644 {
2645   return getSingleLevelFilename(nr);
2646 }
2647
2648 #if ENABLE_UNUSED_CODE
2649 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2650                                                  int type)
2651 {
2652   lfi->type = type;
2653   lfi->packed = FALSE;
2654
2655   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2656   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2657 }
2658 #endif
2659
2660 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2661                                                  int type, char *format, ...)
2662 {
2663   static char basename[MAX_FILENAME_LEN];
2664   va_list ap;
2665
2666   va_start(ap, format);
2667   vsprintf(basename, format, ap);
2668   va_end(ap);
2669
2670   lfi->type = type;
2671   lfi->packed = FALSE;
2672
2673   setString(&lfi->basename, basename);
2674   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2675 }
2676
2677 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2678                                                  int type)
2679 {
2680   lfi->type = type;
2681   lfi->packed = TRUE;
2682
2683   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2684   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2685 }
2686
2687 static int getFiletypeFromID(char *filetype_id)
2688 {
2689   char *filetype_id_lower;
2690   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2691   int i;
2692
2693   if (filetype_id == NULL)
2694     return LEVEL_FILE_TYPE_UNKNOWN;
2695
2696   filetype_id_lower = getStringToLower(filetype_id);
2697
2698   for (i = 0; filetype_id_list[i].id != NULL; i++)
2699   {
2700     char *id_lower = getStringToLower(filetype_id_list[i].id);
2701     
2702     if (strEqual(filetype_id_lower, id_lower))
2703       filetype = filetype_id_list[i].filetype;
2704
2705     free(id_lower);
2706
2707     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2708       break;
2709   }
2710
2711   free(filetype_id_lower);
2712
2713   return filetype;
2714 }
2715
2716 char *getLocalLevelTemplateFilename(void)
2717 {
2718   return getDefaultLevelFilename(-1);
2719 }
2720
2721 char *getGlobalLevelTemplateFilename(void)
2722 {
2723   // global variable "leveldir_current" must be modified in the loop below
2724   LevelDirTree *leveldir_current_last = leveldir_current;
2725   char *filename = NULL;
2726
2727   // check for template level in path from current to topmost tree node
2728
2729   while (leveldir_current != NULL)
2730   {
2731     filename = getDefaultLevelFilename(-1);
2732
2733     if (fileExists(filename))
2734       break;
2735
2736     leveldir_current = leveldir_current->node_parent;
2737   }
2738
2739   // restore global variable "leveldir_current" modified in above loop
2740   leveldir_current = leveldir_current_last;
2741
2742   return filename;
2743 }
2744
2745 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2746 {
2747   int nr = lfi->nr;
2748
2749   // special case: level number is negative => check for level template file
2750   if (nr < 0)
2751   {
2752     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2753                                          getSingleLevelBasename(-1));
2754
2755     // replace local level template filename with global template filename
2756     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2757
2758     // no fallback if template file not existing
2759     return;
2760   }
2761
2762   // special case: check for file name/pattern specified in "levelinfo.conf"
2763   if (leveldir_current->level_filename != NULL)
2764   {
2765     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2766
2767     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2768                                          leveldir_current->level_filename, nr);
2769
2770     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2771
2772     if (fileExists(lfi->filename))
2773       return;
2774   }
2775   else if (leveldir_current->level_filetype != NULL)
2776   {
2777     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2778
2779     // check for specified native level file with standard file name
2780     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2781                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2782     if (fileExists(lfi->filename))
2783       return;
2784   }
2785
2786   // check for native Rocks'n'Diamonds level file
2787   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2788                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2789   if (fileExists(lfi->filename))
2790     return;
2791
2792   // check for native Boulder Dash level file
2793   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2794   if (fileExists(lfi->filename))
2795     return;
2796
2797   // check for Emerald Mine level file (V1)
2798   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2799                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2800   if (fileExists(lfi->filename))
2801     return;
2802   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2803                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2804   if (fileExists(lfi->filename))
2805     return;
2806
2807   // check for Emerald Mine level file (V2 to V5)
2808   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2809   if (fileExists(lfi->filename))
2810     return;
2811
2812   // check for Emerald Mine level file (V6 / single mode)
2813   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2814   if (fileExists(lfi->filename))
2815     return;
2816   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2817   if (fileExists(lfi->filename))
2818     return;
2819
2820   // check for Emerald Mine level file (V6 / teamwork mode)
2821   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2822   if (fileExists(lfi->filename))
2823     return;
2824   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2825   if (fileExists(lfi->filename))
2826     return;
2827
2828   // check for various packed level file formats
2829   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2830   if (fileExists(lfi->filename))
2831     return;
2832
2833   // no known level file found -- use default values (and fail later)
2834   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2835                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2836 }
2837
2838 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2839 {
2840   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2841     lfi->type = getFileTypeFromBasename(lfi->basename);
2842
2843   if (lfi->type == LEVEL_FILE_TYPE_RND)
2844     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2845 }
2846
2847 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2848 {
2849   // always start with reliable default values
2850   setFileInfoToDefaults(level_file_info);
2851
2852   level_file_info->nr = nr;     // set requested level number
2853
2854   determineLevelFileInfo_Filename(level_file_info);
2855   determineLevelFileInfo_Filetype(level_file_info);
2856 }
2857
2858 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2859                               struct LevelFileInfo *lfi_to)
2860 {
2861   lfi_to->nr     = lfi_from->nr;
2862   lfi_to->type   = lfi_from->type;
2863   lfi_to->packed = lfi_from->packed;
2864
2865   setString(&lfi_to->basename, lfi_from->basename);
2866   setString(&lfi_to->filename, lfi_from->filename);
2867 }
2868
2869 // ----------------------------------------------------------------------------
2870 // functions for loading R'n'D level
2871 // ----------------------------------------------------------------------------
2872
2873 int getMappedElement(int element)
2874 {
2875   // remap some (historic, now obsolete) elements
2876
2877   switch (element)
2878   {
2879     case EL_PLAYER_OBSOLETE:
2880       element = EL_PLAYER_1;
2881       break;
2882
2883     case EL_KEY_OBSOLETE:
2884       element = EL_KEY_1;
2885       break;
2886
2887     case EL_EM_KEY_1_FILE_OBSOLETE:
2888       element = EL_EM_KEY_1;
2889       break;
2890
2891     case EL_EM_KEY_2_FILE_OBSOLETE:
2892       element = EL_EM_KEY_2;
2893       break;
2894
2895     case EL_EM_KEY_3_FILE_OBSOLETE:
2896       element = EL_EM_KEY_3;
2897       break;
2898
2899     case EL_EM_KEY_4_FILE_OBSOLETE:
2900       element = EL_EM_KEY_4;
2901       break;
2902
2903     case EL_ENVELOPE_OBSOLETE:
2904       element = EL_ENVELOPE_1;
2905       break;
2906
2907     case EL_SP_EMPTY:
2908       element = EL_EMPTY;
2909       break;
2910
2911     default:
2912       if (element >= NUM_FILE_ELEMENTS)
2913       {
2914         Warn("invalid level element %d", element);
2915
2916         element = EL_UNKNOWN;
2917       }
2918       break;
2919   }
2920
2921   return element;
2922 }
2923
2924 static int getMappedElementByVersion(int element, int game_version)
2925 {
2926   // remap some elements due to certain game version
2927
2928   if (game_version <= VERSION_IDENT(2,2,0,0))
2929   {
2930     // map game font elements
2931     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2932                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2933                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2934                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2935   }
2936
2937   if (game_version < VERSION_IDENT(3,0,0,0))
2938   {
2939     // map Supaplex gravity tube elements
2940     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2941                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2942                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2943                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2944                element);
2945   }
2946
2947   return element;
2948 }
2949
2950 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2951 {
2952   level->file_version = getFileVersion(file);
2953   level->game_version = getFileVersion(file);
2954
2955   return chunk_size;
2956 }
2957
2958 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2959 {
2960   level->creation_date.year  = getFile16BitBE(file);
2961   level->creation_date.month = getFile8Bit(file);
2962   level->creation_date.day   = getFile8Bit(file);
2963
2964   level->creation_date.src   = DATE_SRC_LEVELFILE;
2965
2966   return chunk_size;
2967 }
2968
2969 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2970 {
2971   int initial_player_stepsize;
2972   int initial_player_gravity;
2973   int i, x, y;
2974
2975   level->fieldx = getFile8Bit(file);
2976   level->fieldy = getFile8Bit(file);
2977
2978   level->time           = getFile16BitBE(file);
2979   level->gems_needed    = getFile16BitBE(file);
2980
2981   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2982     level->name[i] = getFile8Bit(file);
2983   level->name[MAX_LEVEL_NAME_LEN] = 0;
2984
2985   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
2986     level->score[i] = getFile8Bit(file);
2987
2988   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
2989   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
2990     for (y = 0; y < 3; y++)
2991       for (x = 0; x < 3; x++)
2992         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
2993
2994   level->amoeba_speed           = getFile8Bit(file);
2995   level->time_magic_wall        = getFile8Bit(file);
2996   level->time_wheel             = getFile8Bit(file);
2997   level->amoeba_content         = getMappedElement(getFile8Bit(file));
2998
2999   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3000                                    STEPSIZE_NORMAL);
3001
3002   for (i = 0; i < MAX_PLAYERS; i++)
3003     level->initial_player_stepsize[i] = initial_player_stepsize;
3004
3005   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3006
3007   for (i = 0; i < MAX_PLAYERS; i++)
3008     level->initial_player_gravity[i] = initial_player_gravity;
3009
3010   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3011   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3012
3013   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3014
3015   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3016   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3017   level->can_move_into_acid_bits = getFile32BitBE(file);
3018   level->dont_collide_with_bits = getFile8Bit(file);
3019
3020   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3022
3023   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3024   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3025   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3026
3027   level->game_engine_type       = getFile8Bit(file);
3028
3029   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3030
3031   return chunk_size;
3032 }
3033
3034 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3035 {
3036   int i;
3037
3038   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3039     level->name[i] = getFile8Bit(file);
3040   level->name[MAX_LEVEL_NAME_LEN] = 0;
3041
3042   return chunk_size;
3043 }
3044
3045 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3046 {
3047   int i;
3048
3049   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3050     level->author[i] = getFile8Bit(file);
3051   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3052
3053   return chunk_size;
3054 }
3055
3056 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3057 {
3058   int x, y;
3059   int chunk_size_expected = level->fieldx * level->fieldy;
3060
3061   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3062      stored with 16-bit encoding (and should be twice as big then).
3063      Even worse, playfield data was stored 16-bit when only yamyam content
3064      contained 16-bit elements and vice versa. */
3065
3066   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3067     chunk_size_expected *= 2;
3068
3069   if (chunk_size_expected != chunk_size)
3070   {
3071     ReadUnusedBytesFromFile(file, chunk_size);
3072     return chunk_size_expected;
3073   }
3074
3075   for (y = 0; y < level->fieldy; y++)
3076     for (x = 0; x < level->fieldx; x++)
3077       level->field[x][y] =
3078         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3079                          getFile8Bit(file));
3080   return chunk_size;
3081 }
3082
3083 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3084 {
3085   int i, x, y;
3086   int header_size = 4;
3087   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3088   int chunk_size_expected = header_size + content_size;
3089
3090   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3091      stored with 16-bit encoding (and should be twice as big then).
3092      Even worse, playfield data was stored 16-bit when only yamyam content
3093      contained 16-bit elements and vice versa. */
3094
3095   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3096     chunk_size_expected += content_size;
3097
3098   if (chunk_size_expected != chunk_size)
3099   {
3100     ReadUnusedBytesFromFile(file, chunk_size);
3101     return chunk_size_expected;
3102   }
3103
3104   getFile8Bit(file);
3105   level->num_yamyam_contents = getFile8Bit(file);
3106   getFile8Bit(file);
3107   getFile8Bit(file);
3108
3109   // correct invalid number of content fields -- should never happen
3110   if (level->num_yamyam_contents < 1 ||
3111       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3112     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3113
3114   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3115     for (y = 0; y < 3; y++)
3116       for (x = 0; x < 3; x++)
3117         level->yamyam_content[i].e[x][y] =
3118           getMappedElement(level->encoding_16bit_field ?
3119                            getFile16BitBE(file) : getFile8Bit(file));
3120   return chunk_size;
3121 }
3122
3123 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3124 {
3125   int i, x, y;
3126   int element;
3127   int num_contents;
3128   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3129
3130   element = getMappedElement(getFile16BitBE(file));
3131   num_contents = getFile8Bit(file);
3132
3133   getFile8Bit(file);    // content x size (unused)
3134   getFile8Bit(file);    // content y size (unused)
3135
3136   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3137
3138   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3139     for (y = 0; y < 3; y++)
3140       for (x = 0; x < 3; x++)
3141         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3142
3143   // correct invalid number of content fields -- should never happen
3144   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3145     num_contents = STD_ELEMENT_CONTENTS;
3146
3147   if (element == EL_YAMYAM)
3148   {
3149     level->num_yamyam_contents = num_contents;
3150
3151     for (i = 0; i < num_contents; i++)
3152       for (y = 0; y < 3; y++)
3153         for (x = 0; x < 3; x++)
3154           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3155   }
3156   else if (element == EL_BD_AMOEBA)
3157   {
3158     level->amoeba_content = content_array[0][0][0];
3159   }
3160   else
3161   {
3162     Warn("cannot load content for element '%d'", element);
3163   }
3164
3165   return chunk_size;
3166 }
3167
3168 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3169 {
3170   int i;
3171   int element;
3172   int envelope_nr;
3173   int envelope_len;
3174   int chunk_size_expected;
3175
3176   element = getMappedElement(getFile16BitBE(file));
3177   if (!IS_ENVELOPE(element))
3178     element = EL_ENVELOPE_1;
3179
3180   envelope_nr = element - EL_ENVELOPE_1;
3181
3182   envelope_len = getFile16BitBE(file);
3183
3184   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3185   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3186
3187   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3188
3189   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3190   if (chunk_size_expected != chunk_size)
3191   {
3192     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3193     return chunk_size_expected;
3194   }
3195
3196   for (i = 0; i < envelope_len; i++)
3197     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3198
3199   return chunk_size;
3200 }
3201
3202 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3203 {
3204   int num_changed_custom_elements = getFile16BitBE(file);
3205   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3206   int i;
3207
3208   if (chunk_size_expected != chunk_size)
3209   {
3210     ReadUnusedBytesFromFile(file, chunk_size - 2);
3211     return chunk_size_expected;
3212   }
3213
3214   for (i = 0; i < num_changed_custom_elements; i++)
3215   {
3216     int element = getMappedElement(getFile16BitBE(file));
3217     int properties = getFile32BitBE(file);
3218
3219     if (IS_CUSTOM_ELEMENT(element))
3220       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3221     else
3222       Warn("invalid custom element number %d", element);
3223
3224     // older game versions that wrote level files with CUS1 chunks used
3225     // different default push delay values (not yet stored in level file)
3226     element_info[element].push_delay_fixed = 2;
3227     element_info[element].push_delay_random = 8;
3228   }
3229
3230   level->file_has_custom_elements = TRUE;
3231
3232   return chunk_size;
3233 }
3234
3235 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3236 {
3237   int num_changed_custom_elements = getFile16BitBE(file);
3238   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3239   int i;
3240
3241   if (chunk_size_expected != chunk_size)
3242   {
3243     ReadUnusedBytesFromFile(file, chunk_size - 2);
3244     return chunk_size_expected;
3245   }
3246
3247   for (i = 0; i < num_changed_custom_elements; i++)
3248   {
3249     int element = getMappedElement(getFile16BitBE(file));
3250     int custom_target_element = getMappedElement(getFile16BitBE(file));
3251
3252     if (IS_CUSTOM_ELEMENT(element))
3253       element_info[element].change->target_element = custom_target_element;
3254     else
3255       Warn("invalid custom element number %d", element);
3256   }
3257
3258   level->file_has_custom_elements = TRUE;
3259
3260   return chunk_size;
3261 }
3262
3263 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3264 {
3265   int num_changed_custom_elements = getFile16BitBE(file);
3266   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3267   int i, j, x, y;
3268
3269   if (chunk_size_expected != chunk_size)
3270   {
3271     ReadUnusedBytesFromFile(file, chunk_size - 2);
3272     return chunk_size_expected;
3273   }
3274
3275   for (i = 0; i < num_changed_custom_elements; i++)
3276   {
3277     int element = getMappedElement(getFile16BitBE(file));
3278     struct ElementInfo *ei = &element_info[element];
3279     unsigned int event_bits;
3280
3281     if (!IS_CUSTOM_ELEMENT(element))
3282     {
3283       Warn("invalid custom element number %d", element);
3284
3285       element = EL_INTERNAL_DUMMY;
3286     }
3287
3288     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3289       ei->description[j] = getFile8Bit(file);
3290     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3291
3292     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3293
3294     // some free bytes for future properties and padding
3295     ReadUnusedBytesFromFile(file, 7);
3296
3297     ei->use_gfx_element = getFile8Bit(file);
3298     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3299
3300     ei->collect_score_initial = getFile8Bit(file);
3301     ei->collect_count_initial = getFile8Bit(file);
3302
3303     ei->push_delay_fixed = getFile16BitBE(file);
3304     ei->push_delay_random = getFile16BitBE(file);
3305     ei->move_delay_fixed = getFile16BitBE(file);
3306     ei->move_delay_random = getFile16BitBE(file);
3307
3308     ei->move_pattern = getFile16BitBE(file);
3309     ei->move_direction_initial = getFile8Bit(file);
3310     ei->move_stepsize = getFile8Bit(file);
3311
3312     for (y = 0; y < 3; y++)
3313       for (x = 0; x < 3; x++)
3314         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3315
3316     // bits 0 - 31 of "has_event[]"
3317     event_bits = getFile32BitBE(file);
3318     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3319       if (event_bits & (1u << j))
3320         ei->change->has_event[j] = TRUE;
3321
3322     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3323
3324     ei->change->delay_fixed = getFile16BitBE(file);
3325     ei->change->delay_random = getFile16BitBE(file);
3326     ei->change->delay_frames = getFile16BitBE(file);
3327
3328     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3329
3330     ei->change->explode = getFile8Bit(file);
3331     ei->change->use_target_content = getFile8Bit(file);
3332     ei->change->only_if_complete = getFile8Bit(file);
3333     ei->change->use_random_replace = getFile8Bit(file);
3334
3335     ei->change->random_percentage = getFile8Bit(file);
3336     ei->change->replace_when = getFile8Bit(file);
3337
3338     for (y = 0; y < 3; y++)
3339       for (x = 0; x < 3; x++)
3340         ei->change->target_content.e[x][y] =
3341           getMappedElement(getFile16BitBE(file));
3342
3343     ei->slippery_type = getFile8Bit(file);
3344
3345     // some free bytes for future properties and padding
3346     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3347
3348     // mark that this custom element has been modified
3349     ei->modified_settings = TRUE;
3350   }
3351
3352   level->file_has_custom_elements = TRUE;
3353
3354   return chunk_size;
3355 }
3356
3357 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3358 {
3359   struct ElementInfo *ei;
3360   int chunk_size_expected;
3361   int element;
3362   int i, j, x, y;
3363
3364   // ---------- custom element base property values (96 bytes) ----------------
3365
3366   element = getMappedElement(getFile16BitBE(file));
3367
3368   if (!IS_CUSTOM_ELEMENT(element))
3369   {
3370     Warn("invalid custom element number %d", element);
3371
3372     ReadUnusedBytesFromFile(file, chunk_size - 2);
3373
3374     return chunk_size;
3375   }
3376
3377   ei = &element_info[element];
3378
3379   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3380     ei->description[i] = getFile8Bit(file);
3381   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3382
3383   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3384
3385   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3386
3387   ei->num_change_pages = getFile8Bit(file);
3388
3389   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3390   if (chunk_size_expected != chunk_size)
3391   {
3392     ReadUnusedBytesFromFile(file, chunk_size - 43);
3393     return chunk_size_expected;
3394   }
3395
3396   ei->ce_value_fixed_initial = getFile16BitBE(file);
3397   ei->ce_value_random_initial = getFile16BitBE(file);
3398   ei->use_last_ce_value = getFile8Bit(file);
3399
3400   ei->use_gfx_element = getFile8Bit(file);
3401   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3402
3403   ei->collect_score_initial = getFile8Bit(file);
3404   ei->collect_count_initial = getFile8Bit(file);
3405
3406   ei->drop_delay_fixed = getFile8Bit(file);
3407   ei->push_delay_fixed = getFile8Bit(file);
3408   ei->drop_delay_random = getFile8Bit(file);
3409   ei->push_delay_random = getFile8Bit(file);
3410   ei->move_delay_fixed = getFile16BitBE(file);
3411   ei->move_delay_random = getFile16BitBE(file);
3412
3413   // bits 0 - 15 of "move_pattern" ...
3414   ei->move_pattern = getFile16BitBE(file);
3415   ei->move_direction_initial = getFile8Bit(file);
3416   ei->move_stepsize = getFile8Bit(file);
3417
3418   ei->slippery_type = getFile8Bit(file);
3419
3420   for (y = 0; y < 3; y++)
3421     for (x = 0; x < 3; x++)
3422       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3423
3424   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3425   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3426   ei->move_leave_type = getFile8Bit(file);
3427
3428   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3429   ei->move_pattern |= (getFile16BitBE(file) << 16);
3430
3431   ei->access_direction = getFile8Bit(file);
3432
3433   ei->explosion_delay = getFile8Bit(file);
3434   ei->ignition_delay = getFile8Bit(file);
3435   ei->explosion_type = getFile8Bit(file);
3436
3437   // some free bytes for future custom property values and padding
3438   ReadUnusedBytesFromFile(file, 1);
3439
3440   // ---------- change page property values (48 bytes) ------------------------
3441
3442   setElementChangePages(ei, ei->num_change_pages);
3443
3444   for (i = 0; i < ei->num_change_pages; i++)
3445   {
3446     struct ElementChangeInfo *change = &ei->change_page[i];
3447     unsigned int event_bits;
3448
3449     // always start with reliable default values
3450     setElementChangeInfoToDefaults(change);
3451
3452     // bits 0 - 31 of "has_event[]" ...
3453     event_bits = getFile32BitBE(file);
3454     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3455       if (event_bits & (1u << j))
3456         change->has_event[j] = TRUE;
3457
3458     change->target_element = getMappedElement(getFile16BitBE(file));
3459
3460     change->delay_fixed = getFile16BitBE(file);
3461     change->delay_random = getFile16BitBE(file);
3462     change->delay_frames = getFile16BitBE(file);
3463
3464     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3465
3466     change->explode = getFile8Bit(file);
3467     change->use_target_content = getFile8Bit(file);
3468     change->only_if_complete = getFile8Bit(file);
3469     change->use_random_replace = getFile8Bit(file);
3470
3471     change->random_percentage = getFile8Bit(file);
3472     change->replace_when = getFile8Bit(file);
3473
3474     for (y = 0; y < 3; y++)
3475       for (x = 0; x < 3; x++)
3476         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3477
3478     change->can_change = getFile8Bit(file);
3479
3480     change->trigger_side = getFile8Bit(file);
3481
3482     change->trigger_player = getFile8Bit(file);
3483     change->trigger_page = getFile8Bit(file);
3484
3485     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3486                             CH_PAGE_ANY : (1 << change->trigger_page));
3487
3488     change->has_action = getFile8Bit(file);
3489     change->action_type = getFile8Bit(file);
3490     change->action_mode = getFile8Bit(file);
3491     change->action_arg = getFile16BitBE(file);
3492
3493     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3494     event_bits = getFile8Bit(file);
3495     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3496       if (event_bits & (1u << (j - 32)))
3497         change->has_event[j] = TRUE;
3498   }
3499
3500   // mark this custom element as modified
3501   ei->modified_settings = TRUE;
3502
3503   level->file_has_custom_elements = TRUE;
3504
3505   return chunk_size;
3506 }
3507
3508 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3509 {
3510   struct ElementInfo *ei;
3511   struct ElementGroupInfo *group;
3512   int element;
3513   int i;
3514
3515   element = getMappedElement(getFile16BitBE(file));
3516
3517   if (!IS_GROUP_ELEMENT(element))
3518   {
3519     Warn("invalid group element number %d", element);
3520
3521     ReadUnusedBytesFromFile(file, chunk_size - 2);
3522
3523     return chunk_size;
3524   }
3525
3526   ei = &element_info[element];
3527
3528   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3529     ei->description[i] = getFile8Bit(file);
3530   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3531
3532   group = element_info[element].group;
3533
3534   group->num_elements = getFile8Bit(file);
3535
3536   ei->use_gfx_element = getFile8Bit(file);
3537   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3538
3539   group->choice_mode = getFile8Bit(file);
3540
3541   // some free bytes for future values and padding
3542   ReadUnusedBytesFromFile(file, 3);
3543
3544   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3545     group->element[i] = getMappedElement(getFile16BitBE(file));
3546
3547   // mark this group element as modified
3548   element_info[element].modified_settings = TRUE;
3549
3550   level->file_has_custom_elements = TRUE;
3551
3552   return chunk_size;
3553 }
3554
3555 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3556                                 int element, int real_element)
3557 {
3558   int micro_chunk_size = 0;
3559   int conf_type = getFile8Bit(file);
3560   int byte_mask = conf_type & CONF_MASK_BYTES;
3561   boolean element_found = FALSE;
3562   int i;
3563
3564   micro_chunk_size += 1;
3565
3566   if (byte_mask == CONF_MASK_MULTI_BYTES)
3567   {
3568     int num_bytes = getFile16BitBE(file);
3569     byte *buffer = checked_malloc(num_bytes);
3570
3571     ReadBytesFromFile(file, buffer, num_bytes);
3572
3573     for (i = 0; conf[i].data_type != -1; i++)
3574     {
3575       if (conf[i].element == element &&
3576           conf[i].conf_type == conf_type)
3577       {
3578         int data_type = conf[i].data_type;
3579         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3580         int max_num_entities = conf[i].max_num_entities;
3581
3582         if (num_entities > max_num_entities)
3583         {
3584           Warn("truncating number of entities for element %d from %d to %d",
3585                element, num_entities, max_num_entities);
3586
3587           num_entities = max_num_entities;
3588         }
3589
3590         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3591                                   data_type == TYPE_CONTENT_LIST))
3592         {
3593           // for element and content lists, zero entities are not allowed
3594           Warn("found empty list of entities for element %d", element);
3595
3596           // do not set "num_entities" here to prevent reading behind buffer
3597
3598           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3599         }
3600         else
3601         {
3602           *(int *)(conf[i].num_entities) = num_entities;
3603         }
3604
3605         element_found = TRUE;
3606
3607         if (data_type == TYPE_STRING)
3608         {
3609           char *string = (char *)(conf[i].value);
3610           int j;
3611
3612           for (j = 0; j < max_num_entities; j++)
3613             string[j] = (j < num_entities ? buffer[j] : '\0');
3614         }
3615         else if (data_type == TYPE_ELEMENT_LIST)
3616         {
3617           int *element_array = (int *)(conf[i].value);
3618           int j;
3619
3620           for (j = 0; j < num_entities; j++)
3621             element_array[j] =
3622               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3623         }
3624         else if (data_type == TYPE_CONTENT_LIST)
3625         {
3626           struct Content *content= (struct Content *)(conf[i].value);
3627           int c, x, y;
3628
3629           for (c = 0; c < num_entities; c++)
3630             for (y = 0; y < 3; y++)
3631               for (x = 0; x < 3; x++)
3632                 content[c].e[x][y] =
3633                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3634         }
3635         else
3636           element_found = FALSE;
3637
3638         break;
3639       }
3640     }
3641
3642     checked_free(buffer);
3643
3644     micro_chunk_size += 2 + num_bytes;
3645   }
3646   else          // constant size configuration data (1, 2 or 4 bytes)
3647   {
3648     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3649                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3650                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3651
3652     for (i = 0; conf[i].data_type != -1; i++)
3653     {
3654       if (conf[i].element == element &&
3655           conf[i].conf_type == conf_type)
3656       {
3657         int data_type = conf[i].data_type;
3658
3659         if (data_type == TYPE_ELEMENT)
3660           value = getMappedElement(value);
3661
3662         if (data_type == TYPE_BOOLEAN)
3663           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3664         else
3665           *(int *)    (conf[i].value) = value;
3666
3667         element_found = TRUE;
3668
3669         break;
3670       }
3671     }
3672
3673     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3674   }
3675
3676   if (!element_found)
3677   {
3678     char *error_conf_chunk_bytes =
3679       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3680        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3681        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3682     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3683     int error_element = real_element;
3684
3685     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3686          error_conf_chunk_bytes, error_conf_chunk_token,
3687          error_element, EL_NAME(error_element));
3688   }
3689
3690   return micro_chunk_size;
3691 }
3692
3693 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3694 {
3695   int real_chunk_size = 0;
3696
3697   li = *level;          // copy level data into temporary buffer
3698
3699   while (!checkEndOfFile(file))
3700   {
3701     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3702
3703     if (real_chunk_size >= chunk_size)
3704       break;
3705   }
3706
3707   *level = li;          // copy temporary buffer back to level data
3708
3709   return real_chunk_size;
3710 }
3711
3712 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3713 {
3714   int real_chunk_size = 0;
3715
3716   li = *level;          // copy level data into temporary buffer
3717
3718   while (!checkEndOfFile(file))
3719   {
3720     int element = getMappedElement(getFile16BitBE(file));
3721
3722     real_chunk_size += 2;
3723     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3724                                             element, element);
3725     if (real_chunk_size >= chunk_size)
3726       break;
3727   }
3728
3729   *level = li;          // copy temporary buffer back to level data
3730
3731   return real_chunk_size;
3732 }
3733
3734 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3735 {
3736   int real_chunk_size = 0;
3737
3738   li = *level;          // copy level data into temporary buffer
3739
3740   while (!checkEndOfFile(file))
3741   {
3742     int element = getMappedElement(getFile16BitBE(file));
3743
3744     real_chunk_size += 2;
3745     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3746                                             element, element);
3747     if (real_chunk_size >= chunk_size)
3748       break;
3749   }
3750
3751   *level = li;          // copy temporary buffer back to level data
3752
3753   return real_chunk_size;
3754 }
3755
3756 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3757 {
3758   int element = getMappedElement(getFile16BitBE(file));
3759   int envelope_nr = element - EL_ENVELOPE_1;
3760   int real_chunk_size = 2;
3761
3762   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3763
3764   while (!checkEndOfFile(file))
3765   {
3766     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3767                                             -1, element);
3768
3769     if (real_chunk_size >= chunk_size)
3770       break;
3771   }
3772
3773   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3774
3775   return real_chunk_size;
3776 }
3777
3778 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3779 {
3780   int element = getMappedElement(getFile16BitBE(file));
3781   int real_chunk_size = 2;
3782   struct ElementInfo *ei = &element_info[element];
3783   int i;
3784
3785   xx_ei = *ei;          // copy element data into temporary buffer
3786
3787   xx_ei.num_change_pages = -1;
3788
3789   while (!checkEndOfFile(file))
3790   {
3791     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3792                                             -1, element);
3793     if (xx_ei.num_change_pages != -1)
3794       break;
3795
3796     if (real_chunk_size >= chunk_size)
3797       break;
3798   }
3799
3800   *ei = xx_ei;
3801
3802   if (ei->num_change_pages == -1)
3803   {
3804     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3805          EL_NAME(element));
3806
3807     ei->num_change_pages = 1;
3808
3809     setElementChangePages(ei, 1);
3810     setElementChangeInfoToDefaults(ei->change);
3811
3812     return real_chunk_size;
3813   }
3814
3815   // initialize number of change pages stored for this custom element
3816   setElementChangePages(ei, ei->num_change_pages);
3817   for (i = 0; i < ei->num_change_pages; i++)
3818     setElementChangeInfoToDefaults(&ei->change_page[i]);
3819
3820   // start with reading properties for the first change page
3821   xx_current_change_page = 0;
3822
3823   while (!checkEndOfFile(file))
3824   {
3825     // level file might contain invalid change page number
3826     if (xx_current_change_page >= ei->num_change_pages)
3827       break;
3828
3829     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3830
3831     xx_change = *change;        // copy change data into temporary buffer
3832
3833     resetEventBits();           // reset bits; change page might have changed
3834
3835     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3836                                             -1, element);
3837
3838     *change = xx_change;
3839
3840     setEventFlagsFromEventBits(change);
3841
3842     if (real_chunk_size >= chunk_size)
3843       break;
3844   }
3845
3846   level->file_has_custom_elements = TRUE;
3847
3848   return real_chunk_size;
3849 }
3850
3851 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3852 {
3853   int element = getMappedElement(getFile16BitBE(file));
3854   int real_chunk_size = 2;
3855   struct ElementInfo *ei = &element_info[element];
3856   struct ElementGroupInfo *group = ei->group;
3857
3858   if (group == NULL)
3859     return -1;
3860
3861   xx_ei = *ei;          // copy element data into temporary buffer
3862   xx_group = *group;    // copy group data into temporary buffer
3863
3864   while (!checkEndOfFile(file))
3865   {
3866     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3867                                             -1, element);
3868
3869     if (real_chunk_size >= chunk_size)
3870       break;
3871   }
3872
3873   *ei = xx_ei;
3874   *group = xx_group;
3875
3876   level->file_has_custom_elements = TRUE;
3877
3878   return real_chunk_size;
3879 }
3880
3881 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3882 {
3883   int element = getMappedElement(getFile16BitBE(file));
3884   int real_chunk_size = 2;
3885   struct ElementInfo *ei = &element_info[element];
3886
3887   xx_ei = *ei;          // copy element data into temporary buffer
3888
3889   while (!checkEndOfFile(file))
3890   {
3891     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3892                                             -1, element);
3893
3894     if (real_chunk_size >= chunk_size)
3895       break;
3896   }
3897
3898   *ei = xx_ei;
3899
3900   level->file_has_custom_elements = TRUE;
3901
3902   return real_chunk_size;
3903 }
3904
3905 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3906                                       struct LevelFileInfo *level_file_info,
3907                                       boolean level_info_only)
3908 {
3909   char *filename = level_file_info->filename;
3910   char cookie[MAX_LINE_LEN];
3911   char chunk_name[CHUNK_ID_LEN + 1];
3912   int chunk_size;
3913   File *file;
3914
3915   if (!(file = openFile(filename, MODE_READ)))
3916   {
3917     level->no_valid_file = TRUE;
3918     level->no_level_file = TRUE;
3919
3920     if (level_info_only)
3921       return;
3922
3923     Warn("cannot read level '%s' -- using empty level", filename);
3924
3925     if (!setup.editor.use_template_for_new_levels)
3926       return;
3927
3928     // if level file not found, try to initialize level data from template
3929     filename = getGlobalLevelTemplateFilename();
3930
3931     if (!(file = openFile(filename, MODE_READ)))
3932       return;
3933
3934     // default: for empty levels, use level template for custom elements
3935     level->use_custom_template = TRUE;
3936
3937     level->no_valid_file = FALSE;
3938   }
3939
3940   getFileChunkBE(file, chunk_name, NULL);
3941   if (strEqual(chunk_name, "RND1"))
3942   {
3943     getFile32BitBE(file);               // not used
3944
3945     getFileChunkBE(file, chunk_name, NULL);
3946     if (!strEqual(chunk_name, "CAVE"))
3947     {
3948       level->no_valid_file = TRUE;
3949
3950       Warn("unknown format of level file '%s'", filename);
3951
3952       closeFile(file);
3953
3954       return;
3955     }
3956   }
3957   else  // check for pre-2.0 file format with cookie string
3958   {
3959     strcpy(cookie, chunk_name);
3960     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3961       cookie[4] = '\0';
3962     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3963       cookie[strlen(cookie) - 1] = '\0';
3964
3965     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3966     {
3967       level->no_valid_file = TRUE;
3968
3969       Warn("unknown format of level file '%s'", filename);
3970
3971       closeFile(file);
3972
3973       return;
3974     }
3975
3976     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3977     {
3978       level->no_valid_file = TRUE;
3979
3980       Warn("unsupported version of level file '%s'", filename);
3981
3982       closeFile(file);
3983
3984       return;
3985     }
3986
3987     // pre-2.0 level files have no game version, so use file version here
3988     level->game_version = level->file_version;
3989   }
3990
3991   if (level->file_version < FILE_VERSION_1_2)
3992   {
3993     // level files from versions before 1.2.0 without chunk structure
3994     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
3995     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
3996   }
3997   else
3998   {
3999     static struct
4000     {
4001       char *name;
4002       int size;
4003       int (*loader)(File *, int, struct LevelInfo *);
4004     }
4005     chunk_info[] =
4006     {
4007       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4008       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4009       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4010       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4011       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4012       { "INFO", -1,                     LoadLevel_INFO },
4013       { "BODY", -1,                     LoadLevel_BODY },
4014       { "CONT", -1,                     LoadLevel_CONT },
4015       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4016       { "CNT3", -1,                     LoadLevel_CNT3 },
4017       { "CUS1", -1,                     LoadLevel_CUS1 },
4018       { "CUS2", -1,                     LoadLevel_CUS2 },
4019       { "CUS3", -1,                     LoadLevel_CUS3 },
4020       { "CUS4", -1,                     LoadLevel_CUS4 },
4021       { "GRP1", -1,                     LoadLevel_GRP1 },
4022       { "CONF", -1,                     LoadLevel_CONF },
4023       { "ELEM", -1,                     LoadLevel_ELEM },
4024       { "NOTE", -1,                     LoadLevel_NOTE },
4025       { "CUSX", -1,                     LoadLevel_CUSX },
4026       { "GRPX", -1,                     LoadLevel_GRPX },
4027       { "EMPX", -1,                     LoadLevel_EMPX },
4028
4029       {  NULL,  0,                      NULL }
4030     };
4031
4032     while (getFileChunkBE(file, chunk_name, &chunk_size))
4033     {
4034       int i = 0;
4035
4036       while (chunk_info[i].name != NULL &&
4037              !strEqual(chunk_name, chunk_info[i].name))
4038         i++;
4039
4040       if (chunk_info[i].name == NULL)
4041       {
4042         Warn("unknown chunk '%s' in level file '%s'",
4043              chunk_name, filename);
4044
4045         ReadUnusedBytesFromFile(file, chunk_size);
4046       }
4047       else if (chunk_info[i].size != -1 &&
4048                chunk_info[i].size != chunk_size)
4049       {
4050         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4051              chunk_size, chunk_name, filename);
4052
4053         ReadUnusedBytesFromFile(file, chunk_size);
4054       }
4055       else
4056       {
4057         // call function to load this level chunk
4058         int chunk_size_expected =
4059           (chunk_info[i].loader)(file, chunk_size, level);
4060
4061         if (chunk_size_expected < 0)
4062         {
4063           Warn("error reading chunk '%s' in level file '%s'",
4064                chunk_name, filename);
4065
4066           break;
4067         }
4068
4069         // the size of some chunks cannot be checked before reading other
4070         // chunks first (like "HEAD" and "BODY") that contain some header
4071         // information, so check them here
4072         if (chunk_size_expected != chunk_size)
4073         {
4074           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4075                chunk_size, chunk_name, filename);
4076
4077           break;
4078         }
4079       }
4080     }
4081   }
4082
4083   closeFile(file);
4084 }
4085
4086
4087 // ----------------------------------------------------------------------------
4088 // functions for loading BD level
4089 // ----------------------------------------------------------------------------
4090
4091 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4092 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4093
4094 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4095 {
4096   struct LevelInfo_BD *level_bd = level->native_bd_level;
4097   GdCave *cave = NULL;  // will be changed below
4098   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4099   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4100   int x, y;
4101
4102   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4103
4104   // cave and map newly allocated when set to defaults above
4105   cave = level_bd->cave;
4106
4107   // level type
4108   cave->intermission                    = level->bd_intermission;
4109
4110   // level settings
4111   cave->level_time[0]                   = level->time;
4112   cave->level_diamonds[0]               = level->gems_needed;
4113
4114   // game timing
4115   cave->scheduling                      = level->bd_scheduling_type;
4116   cave->pal_timing                      = level->bd_pal_timing;
4117   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4118   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4119   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4120   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4121
4122   // scores
4123   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4124   cave->diamond_value                   = level->score[SC_EMERALD];
4125   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4126
4127   // compatibility settings
4128   cave->lineshift                       = level->bd_line_shifting_borders;
4129   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4130   cave->short_explosions                = level->bd_short_explosions;
4131   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4132
4133   // player properties
4134   cave->diagonal_movements              = level->bd_diagonal_movements;
4135   cave->active_is_first_found           = level->bd_topmost_player_active;
4136   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4137   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4138   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4139   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4140
4141   // element properties
4142   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4143   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4144   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4145   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4146   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4147   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4148   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4149   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4150   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4151
4152   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4153   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4154   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4155   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4156   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4157   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4158   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4159
4160   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4161   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4162   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4163   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4164   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4165   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4166   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4167   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4168   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4169   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4170   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4171
4172   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4173   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4174   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4175   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4176   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4177   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4178
4179   cave->slime_predictable               = level->bd_slime_is_predictable;
4180   cave->slime_correct_random            = level->bd_slime_correct_random;
4181   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4182   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4183   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4184   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4185   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4186   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4187   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4188   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4189   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4190   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4191
4192   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4193   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4194   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4195
4196   cave->biter_delay_frame               = level->bd_biter_move_delay;
4197   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4198
4199   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4200
4201   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4202
4203   cave->replicators_active              = level->bd_replicators_active;
4204   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4205
4206   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4207   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4208
4209   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4210
4211   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4212
4213   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4214   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4215   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4216
4217   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4218   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4219
4220   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4221   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4222
4223   // level name
4224   strncpy(cave->name, level->name, sizeof(GdString));
4225   cave->name[sizeof(GdString) - 1] = '\0';
4226
4227   // playfield elements
4228   for (x = 0; x < cave->w; x++)
4229     for (y = 0; y < cave->h; y++)
4230       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4231 }
4232
4233 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4234 {
4235   struct LevelInfo_BD *level_bd = level->native_bd_level;
4236   GdCave *cave = level_bd->cave;
4237   int bd_level_nr = level_bd->level_nr;
4238   int x, y;
4239
4240   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4241   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4242
4243   // level type
4244   level->bd_intermission                = cave->intermission;
4245
4246   // level settings
4247   level->time                           = cave->level_time[bd_level_nr];
4248   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4249
4250   // game timing
4251   level->bd_scheduling_type             = cave->scheduling;
4252   level->bd_pal_timing                  = cave->pal_timing;
4253   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4254   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4255   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4256   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4257
4258   // scores
4259   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4260   level->score[SC_EMERALD]              = cave->diamond_value;
4261   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4262
4263   // compatibility settings
4264   level->bd_line_shifting_borders       = cave->lineshift;
4265   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4266   level->bd_short_explosions            = cave->short_explosions;
4267   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4268
4269   // player properties
4270   level->bd_diagonal_movements          = cave->diagonal_movements;
4271   level->bd_topmost_player_active       = cave->active_is_first_found;
4272   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4273   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4274   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4275   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4276
4277   // element properties
4278   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4279   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4280   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4281   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4282   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4283   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4284   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4285   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4286   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4287
4288   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4289   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4290   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4291   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4292   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4293   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4294   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4295
4296   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4297   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4298   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4299   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4300   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4301   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4302   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4303   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4304   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4305   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4306   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4307
4308   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4309   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4310   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4311   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4312   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4313   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4314
4315   level->bd_slime_is_predictable        = cave->slime_predictable;
4316   level->bd_slime_correct_random        = cave->slime_correct_random;
4317   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4318   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4319   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4320   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4321   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4322   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4323   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4324   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4325   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4326   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4327
4328   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4329   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4330   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4331
4332   level->bd_biter_move_delay            = cave->biter_delay_frame;
4333   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4334
4335   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4336
4337   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4338
4339   level->bd_replicators_active          = cave->replicators_active;
4340   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4341
4342   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4343   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4344
4345   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4346
4347   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4348
4349   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4350   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4351   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4352
4353   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4354   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4355
4356   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4357   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4358
4359   // level name
4360   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4361
4362   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4363   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4364
4365   // playfield elements
4366   for (x = 0; x < level->fieldx; x++)
4367     for (y = 0; y < level->fieldy; y++)
4368       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4369
4370   checked_free(cave_name);
4371 }
4372
4373 static void setTapeInfoToDefaults(void);
4374
4375 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4376 {
4377   struct LevelInfo_BD *level_bd = level->native_bd_level;
4378   GdCave *cave = level_bd->cave;
4379   GdReplay *replay = level_bd->replay;
4380   int i;
4381
4382   if (replay == NULL)
4383     return;
4384
4385   // always start with reliable default values
4386   setTapeInfoToDefaults();
4387
4388   tape.level_nr = level_nr;             // (currently not used)
4389   tape.random_seed = replay->seed;
4390
4391   TapeSetDateFromIsoDateString(replay->date);
4392
4393   tape.counter = 0;
4394   tape.pos[tape.counter].delay = 0;
4395
4396   tape.bd_replay = TRUE;
4397
4398   // all time calculations only used to display approximate tape time
4399   int cave_speed = cave->speed;
4400   int milliseconds_game = 0;
4401   int milliseconds_elapsed = 20;
4402
4403   for (i = 0; i < replay->movements->len; i++)
4404   {
4405     int replay_action = replay->movements->data[i];
4406     int tape_action = map_action_BD_to_RND(replay_action);
4407     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4408     boolean success = 0;
4409
4410     while (1)
4411     {
4412       success = TapeAddAction(action);
4413
4414       milliseconds_game += milliseconds_elapsed;
4415
4416       if (milliseconds_game >= cave_speed)
4417       {
4418         milliseconds_game -= cave_speed;
4419
4420         break;
4421       }
4422     }
4423
4424     tape.counter++;
4425     tape.pos[tape.counter].delay = 0;
4426     tape.pos[tape.counter].action[0] = 0;
4427
4428     if (!success)
4429     {
4430       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4431
4432       break;
4433     }
4434   }
4435
4436   TapeHaltRecording();
4437 }
4438
4439
4440 // ----------------------------------------------------------------------------
4441 // functions for loading EM level
4442 // ----------------------------------------------------------------------------
4443
4444 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4445 {
4446   static int ball_xy[8][2] =
4447   {
4448     { 0, 0 },
4449     { 1, 0 },
4450     { 2, 0 },
4451     { 0, 1 },
4452     { 2, 1 },
4453     { 0, 2 },
4454     { 1, 2 },
4455     { 2, 2 },
4456   };
4457   struct LevelInfo_EM *level_em = level->native_em_level;
4458   struct CAVE *cav = level_em->cav;
4459   int i, j, x, y;
4460
4461   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4462   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4463
4464   cav->time_seconds     = level->time;
4465   cav->gems_needed      = level->gems_needed;
4466
4467   cav->emerald_score    = level->score[SC_EMERALD];
4468   cav->diamond_score    = level->score[SC_DIAMOND];
4469   cav->alien_score      = level->score[SC_ROBOT];
4470   cav->tank_score       = level->score[SC_SPACESHIP];
4471   cav->bug_score        = level->score[SC_BUG];
4472   cav->eater_score      = level->score[SC_YAMYAM];
4473   cav->nut_score        = level->score[SC_NUT];
4474   cav->dynamite_score   = level->score[SC_DYNAMITE];
4475   cav->key_score        = level->score[SC_KEY];
4476   cav->exit_score       = level->score[SC_TIME_BONUS];
4477
4478   cav->num_eater_arrays = level->num_yamyam_contents;
4479
4480   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4481     for (y = 0; y < 3; y++)
4482       for (x = 0; x < 3; x++)
4483         cav->eater_array[i][y * 3 + x] =
4484           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4485
4486   cav->amoeba_time              = level->amoeba_speed;
4487   cav->wonderwall_time          = level->time_magic_wall;
4488   cav->wheel_time               = level->time_wheel;
4489
4490   cav->android_move_time        = level->android_move_time;
4491   cav->android_clone_time       = level->android_clone_time;
4492   cav->ball_random              = level->ball_random;
4493   cav->ball_active              = level->ball_active_initial;
4494   cav->ball_time                = level->ball_time;
4495   cav->num_ball_arrays          = level->num_ball_contents;
4496
4497   cav->lenses_score             = level->lenses_score;
4498   cav->magnify_score            = level->magnify_score;
4499   cav->slurp_score              = level->slurp_score;
4500
4501   cav->lenses_time              = level->lenses_time;
4502   cav->magnify_time             = level->magnify_time;
4503
4504   cav->wind_time = 9999;
4505   cav->wind_direction =
4506     map_direction_RND_to_EM(level->wind_direction_initial);
4507
4508   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4509     for (j = 0; j < 8; j++)
4510       cav->ball_array[i][j] =
4511         map_element_RND_to_EM_cave(level->ball_content[i].
4512                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4513
4514   map_android_clone_elements_RND_to_EM(level);
4515
4516   // first fill the complete playfield with the empty space element
4517   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4518     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4519       cav->cave[x][y] = Cblank;
4520
4521   // then copy the real level contents from level file into the playfield
4522   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4523   {
4524     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4525
4526     if (level->field[x][y] == EL_AMOEBA_DEAD)
4527       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4528
4529     cav->cave[x][y] = new_element;
4530   }
4531
4532   for (i = 0; i < MAX_PLAYERS; i++)
4533   {
4534     cav->player_x[i] = -1;
4535     cav->player_y[i] = -1;
4536   }
4537
4538   // initialize player positions and delete players from the playfield
4539   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4540   {
4541     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4542     {
4543       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4544
4545       cav->player_x[player_nr] = x;
4546       cav->player_y[player_nr] = y;
4547
4548       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4549     }
4550   }
4551 }
4552
4553 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4554 {
4555   static int ball_xy[8][2] =
4556   {
4557     { 0, 0 },
4558     { 1, 0 },
4559     { 2, 0 },
4560     { 0, 1 },
4561     { 2, 1 },
4562     { 0, 2 },
4563     { 1, 2 },
4564     { 2, 2 },
4565   };
4566   struct LevelInfo_EM *level_em = level->native_em_level;
4567   struct CAVE *cav = level_em->cav;
4568   int i, j, x, y;
4569
4570   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4571   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4572
4573   level->time        = cav->time_seconds;
4574   level->gems_needed = cav->gems_needed;
4575
4576   sprintf(level->name, "Level %d", level->file_info.nr);
4577
4578   level->score[SC_EMERALD]      = cav->emerald_score;
4579   level->score[SC_DIAMOND]      = cav->diamond_score;
4580   level->score[SC_ROBOT]        = cav->alien_score;
4581   level->score[SC_SPACESHIP]    = cav->tank_score;
4582   level->score[SC_BUG]          = cav->bug_score;
4583   level->score[SC_YAMYAM]       = cav->eater_score;
4584   level->score[SC_NUT]          = cav->nut_score;
4585   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4586   level->score[SC_KEY]          = cav->key_score;
4587   level->score[SC_TIME_BONUS]   = cav->exit_score;
4588
4589   level->num_yamyam_contents    = cav->num_eater_arrays;
4590
4591   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4592     for (y = 0; y < 3; y++)
4593       for (x = 0; x < 3; x++)
4594         level->yamyam_content[i].e[x][y] =
4595           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4596
4597   level->amoeba_speed           = cav->amoeba_time;
4598   level->time_magic_wall        = cav->wonderwall_time;
4599   level->time_wheel             = cav->wheel_time;
4600
4601   level->android_move_time      = cav->android_move_time;
4602   level->android_clone_time     = cav->android_clone_time;
4603   level->ball_random            = cav->ball_random;
4604   level->ball_active_initial    = cav->ball_active;
4605   level->ball_time              = cav->ball_time;
4606   level->num_ball_contents      = cav->num_ball_arrays;
4607
4608   level->lenses_score           = cav->lenses_score;
4609   level->magnify_score          = cav->magnify_score;
4610   level->slurp_score            = cav->slurp_score;
4611
4612   level->lenses_time            = cav->lenses_time;
4613   level->magnify_time           = cav->magnify_time;
4614
4615   level->wind_direction_initial =
4616     map_direction_EM_to_RND(cav->wind_direction);
4617
4618   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4619     for (j = 0; j < 8; j++)
4620       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4621         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4622
4623   map_android_clone_elements_EM_to_RND(level);
4624
4625   // convert the playfield (some elements need special treatment)
4626   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4627   {
4628     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4629
4630     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4631       new_element = EL_AMOEBA_DEAD;
4632
4633     level->field[x][y] = new_element;
4634   }
4635
4636   for (i = 0; i < MAX_PLAYERS; i++)
4637   {
4638     // in case of all players set to the same field, use the first player
4639     int nr = MAX_PLAYERS - i - 1;
4640     int jx = cav->player_x[nr];
4641     int jy = cav->player_y[nr];
4642
4643     if (jx != -1 && jy != -1)
4644       level->field[jx][jy] = EL_PLAYER_1 + nr;
4645   }
4646
4647   // time score is counted for each 10 seconds left in Emerald Mine levels
4648   level->time_score_base = 10;
4649 }
4650
4651
4652 // ----------------------------------------------------------------------------
4653 // functions for loading SP level
4654 // ----------------------------------------------------------------------------
4655
4656 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4657 {
4658   struct LevelInfo_SP *level_sp = level->native_sp_level;
4659   LevelInfoType *header = &level_sp->header;
4660   int i, x, y;
4661
4662   level_sp->width  = level->fieldx;
4663   level_sp->height = level->fieldy;
4664
4665   for (x = 0; x < level->fieldx; x++)
4666     for (y = 0; y < level->fieldy; y++)
4667       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4668
4669   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4670
4671   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4672     header->LevelTitle[i] = level->name[i];
4673   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4674
4675   header->InfotronsNeeded = level->gems_needed;
4676
4677   header->SpecialPortCount = 0;
4678
4679   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4680   {
4681     boolean gravity_port_found = FALSE;
4682     boolean gravity_port_valid = FALSE;
4683     int gravity_port_flag;
4684     int gravity_port_base_element;
4685     int element = level->field[x][y];
4686
4687     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4688         element <= EL_SP_GRAVITY_ON_PORT_UP)
4689     {
4690       gravity_port_found = TRUE;
4691       gravity_port_valid = TRUE;
4692       gravity_port_flag = 1;
4693       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4694     }
4695     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4696              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4697     {
4698       gravity_port_found = TRUE;
4699       gravity_port_valid = TRUE;
4700       gravity_port_flag = 0;
4701       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4702     }
4703     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4704              element <= EL_SP_GRAVITY_PORT_UP)
4705     {
4706       // change R'n'D style gravity inverting special port to normal port
4707       // (there are no gravity inverting ports in native Supaplex engine)
4708
4709       gravity_port_found = TRUE;
4710       gravity_port_valid = FALSE;
4711       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4712     }
4713
4714     if (gravity_port_found)
4715     {
4716       if (gravity_port_valid &&
4717           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4718       {
4719         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4720
4721         port->PortLocation = (y * level->fieldx + x) * 2;
4722         port->Gravity = gravity_port_flag;
4723
4724         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4725
4726         header->SpecialPortCount++;
4727       }
4728       else
4729       {
4730         // change special gravity port to normal port
4731
4732         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4733       }
4734
4735       level_sp->playfield[x][y] = element - EL_SP_START;
4736     }
4737   }
4738 }
4739
4740 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4741 {
4742   struct LevelInfo_SP *level_sp = level->native_sp_level;
4743   LevelInfoType *header = &level_sp->header;
4744   boolean num_invalid_elements = 0;
4745   int i, j, x, y;
4746
4747   level->fieldx = level_sp->width;
4748   level->fieldy = level_sp->height;
4749
4750   for (x = 0; x < level->fieldx; x++)
4751   {
4752     for (y = 0; y < level->fieldy; y++)
4753     {
4754       int element_old = level_sp->playfield[x][y];
4755       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4756
4757       if (element_new == EL_UNKNOWN)
4758       {
4759         num_invalid_elements++;
4760
4761         Debug("level:native:SP", "invalid element %d at position %d, %d",
4762               element_old, x, y);
4763       }
4764
4765       level->field[x][y] = element_new;
4766     }
4767   }
4768
4769   if (num_invalid_elements > 0)
4770     Warn("found %d invalid elements%s", num_invalid_elements,
4771          (!options.debug ? " (use '--debug' for more details)" : ""));
4772
4773   for (i = 0; i < MAX_PLAYERS; i++)
4774     level->initial_player_gravity[i] =
4775       (header->InitialGravity == 1 ? TRUE : FALSE);
4776
4777   // skip leading spaces
4778   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4779     if (header->LevelTitle[i] != ' ')
4780       break;
4781
4782   // copy level title
4783   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4784     level->name[j] = header->LevelTitle[i];
4785   level->name[j] = '\0';
4786
4787   // cut trailing spaces
4788   for (; j > 0; j--)
4789     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4790       level->name[j - 1] = '\0';
4791
4792   level->gems_needed = header->InfotronsNeeded;
4793
4794   for (i = 0; i < header->SpecialPortCount; i++)
4795   {
4796     SpecialPortType *port = &header->SpecialPort[i];
4797     int port_location = port->PortLocation;
4798     int gravity = port->Gravity;
4799     int port_x, port_y, port_element;
4800
4801     port_x = (port_location / 2) % level->fieldx;
4802     port_y = (port_location / 2) / level->fieldx;
4803
4804     if (port_x < 0 || port_x >= level->fieldx ||
4805         port_y < 0 || port_y >= level->fieldy)
4806     {
4807       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4808
4809       continue;
4810     }
4811
4812     port_element = level->field[port_x][port_y];
4813
4814     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4815         port_element > EL_SP_GRAVITY_PORT_UP)
4816     {
4817       Warn("no special port at position (%d, %d)", port_x, port_y);
4818
4819       continue;
4820     }
4821
4822     // change previous (wrong) gravity inverting special port to either
4823     // gravity enabling special port or gravity disabling special port
4824     level->field[port_x][port_y] +=
4825       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4826        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4827   }
4828
4829   // change special gravity ports without database entries to normal ports
4830   for (x = 0; x < level->fieldx; x++)
4831     for (y = 0; y < level->fieldy; y++)
4832       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4833           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4834         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4835
4836   level->time = 0;                      // no time limit
4837   level->amoeba_speed = 0;
4838   level->time_magic_wall = 0;
4839   level->time_wheel = 0;
4840   level->amoeba_content = EL_EMPTY;
4841
4842   // original Supaplex does not use score values -- rate by playing time
4843   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4844     level->score[i] = 0;
4845
4846   level->rate_time_over_score = TRUE;
4847
4848   // there are no yamyams in supaplex levels
4849   for (i = 0; i < level->num_yamyam_contents; i++)
4850     for (x = 0; x < 3; x++)
4851       for (y = 0; y < 3; y++)
4852         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4853 }
4854
4855 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4856 {
4857   struct LevelInfo_SP *level_sp = level->native_sp_level;
4858   struct DemoInfo_SP *demo = &level_sp->demo;
4859   int i, j;
4860
4861   // always start with reliable default values
4862   demo->is_available = FALSE;
4863   demo->length = 0;
4864
4865   if (TAPE_IS_EMPTY(tape))
4866     return;
4867
4868   demo->level_nr = tape.level_nr;       // (currently not used)
4869
4870   level_sp->header.DemoRandomSeed = tape.random_seed;
4871
4872   demo->length = 0;
4873
4874   for (i = 0; i < tape.length; i++)
4875   {
4876     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4877     int demo_repeat = tape.pos[i].delay;
4878     int demo_entries = (demo_repeat + 15) / 16;
4879
4880     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4881     {
4882       Warn("tape truncated: size exceeds maximum SP demo size %d",
4883            SP_MAX_TAPE_LEN);
4884
4885       break;
4886     }
4887
4888     for (j = 0; j < demo_repeat / 16; j++)
4889       demo->data[demo->length++] = 0xf0 | demo_action;
4890
4891     if (demo_repeat % 16)
4892       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4893   }
4894
4895   demo->is_available = TRUE;
4896 }
4897
4898 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4899 {
4900   struct LevelInfo_SP *level_sp = level->native_sp_level;
4901   struct DemoInfo_SP *demo = &level_sp->demo;
4902   char *filename = level->file_info.filename;
4903   int i;
4904
4905   // always start with reliable default values
4906   setTapeInfoToDefaults();
4907
4908   if (!demo->is_available)
4909     return;
4910
4911   tape.level_nr = demo->level_nr;       // (currently not used)
4912   tape.random_seed = level_sp->header.DemoRandomSeed;
4913
4914   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4915
4916   tape.counter = 0;
4917   tape.pos[tape.counter].delay = 0;
4918
4919   for (i = 0; i < demo->length; i++)
4920   {
4921     int demo_action = demo->data[i] & 0x0f;
4922     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4923     int tape_action = map_key_SP_to_RND(demo_action);
4924     int tape_repeat = demo_repeat + 1;
4925     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4926     boolean success = 0;
4927     int j;
4928
4929     for (j = 0; j < tape_repeat; j++)
4930       success = TapeAddAction(action);
4931
4932     if (!success)
4933     {
4934       Warn("SP demo truncated: size exceeds maximum tape size %d",
4935            MAX_TAPE_LEN);
4936
4937       break;
4938     }
4939   }
4940
4941   TapeHaltRecording();
4942 }
4943
4944
4945 // ----------------------------------------------------------------------------
4946 // functions for loading MM level
4947 // ----------------------------------------------------------------------------
4948
4949 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4950 {
4951   struct LevelInfo_MM *level_mm = level->native_mm_level;
4952   int i, x, y;
4953
4954   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4955   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4956
4957   level_mm->time = level->time;
4958   level_mm->kettles_needed = level->gems_needed;
4959   level_mm->auto_count_kettles = level->auto_count_gems;
4960
4961   level_mm->mm_laser_red   = level->mm_laser_red;
4962   level_mm->mm_laser_green = level->mm_laser_green;
4963   level_mm->mm_laser_blue  = level->mm_laser_blue;
4964
4965   level_mm->df_laser_red   = level->df_laser_red;
4966   level_mm->df_laser_green = level->df_laser_green;
4967   level_mm->df_laser_blue  = level->df_laser_blue;
4968
4969   strcpy(level_mm->name, level->name);
4970   strcpy(level_mm->author, level->author);
4971
4972   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4973   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4974   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4975   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4976   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
4977
4978   level_mm->amoeba_speed = level->amoeba_speed;
4979   level_mm->time_fuse    = level->mm_time_fuse;
4980   level_mm->time_bomb    = level->mm_time_bomb;
4981   level_mm->time_ball    = level->mm_time_ball;
4982   level_mm->time_block   = level->mm_time_block;
4983
4984   level_mm->num_ball_contents = level->num_mm_ball_contents;
4985   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
4986   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
4987   level_mm->explode_ball = level->explode_mm_ball;
4988
4989   for (i = 0; i < level->num_mm_ball_contents; i++)
4990     level_mm->ball_content[i] =
4991       map_element_RND_to_MM(level->mm_ball_content[i]);
4992
4993   for (x = 0; x < level->fieldx; x++)
4994     for (y = 0; y < level->fieldy; y++)
4995       Ur[x][y] =
4996         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
4997 }
4998
4999 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5000 {
5001   struct LevelInfo_MM *level_mm = level->native_mm_level;
5002   int i, x, y;
5003
5004   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5005   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5006
5007   level->time = level_mm->time;
5008   level->gems_needed = level_mm->kettles_needed;
5009   level->auto_count_gems = level_mm->auto_count_kettles;
5010
5011   level->mm_laser_red   = level_mm->mm_laser_red;
5012   level->mm_laser_green = level_mm->mm_laser_green;
5013   level->mm_laser_blue  = level_mm->mm_laser_blue;
5014
5015   level->df_laser_red   = level_mm->df_laser_red;
5016   level->df_laser_green = level_mm->df_laser_green;
5017   level->df_laser_blue  = level_mm->df_laser_blue;
5018
5019   strcpy(level->name, level_mm->name);
5020
5021   // only overwrite author from 'levelinfo.conf' if author defined in level
5022   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5023     strcpy(level->author, level_mm->author);
5024
5025   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5026   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5027   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5028   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5029   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5030
5031   level->amoeba_speed  = level_mm->amoeba_speed;
5032   level->mm_time_fuse  = level_mm->time_fuse;
5033   level->mm_time_bomb  = level_mm->time_bomb;
5034   level->mm_time_ball  = level_mm->time_ball;
5035   level->mm_time_block = level_mm->time_block;
5036
5037   level->num_mm_ball_contents = level_mm->num_ball_contents;
5038   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5039   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5040   level->explode_mm_ball = level_mm->explode_ball;
5041
5042   for (i = 0; i < level->num_mm_ball_contents; i++)
5043     level->mm_ball_content[i] =
5044       map_element_MM_to_RND(level_mm->ball_content[i]);
5045
5046   for (x = 0; x < level->fieldx; x++)
5047     for (y = 0; y < level->fieldy; y++)
5048       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5049 }
5050
5051
5052 // ----------------------------------------------------------------------------
5053 // functions for loading DC level
5054 // ----------------------------------------------------------------------------
5055
5056 #define DC_LEVEL_HEADER_SIZE            344
5057
5058 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5059                                         boolean init)
5060 {
5061   static int last_data_encoded;
5062   static int offset1;
5063   static int offset2;
5064   int diff;
5065   int diff_hi, diff_lo;
5066   int data_hi, data_lo;
5067   unsigned short data_decoded;
5068
5069   if (init)
5070   {
5071     last_data_encoded = 0;
5072     offset1 = -1;
5073     offset2 = 0;
5074
5075     return 0;
5076   }
5077
5078   diff = data_encoded - last_data_encoded;
5079   diff_hi = diff & ~0xff;
5080   diff_lo = diff &  0xff;
5081
5082   offset2 += diff_lo;
5083
5084   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5085   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5086   data_hi = data_hi & 0xff00;
5087
5088   data_decoded = data_hi | data_lo;
5089
5090   last_data_encoded = data_encoded;
5091
5092   offset1 = (offset1 + 1) % 31;
5093   offset2 = offset2 & 0xff;
5094
5095   return data_decoded;
5096 }
5097
5098 static int getMappedElement_DC(int element)
5099 {
5100   switch (element)
5101   {
5102     case 0x0000:
5103       element = EL_ROCK;
5104       break;
5105
5106       // 0x0117 - 0x036e: (?)
5107       // EL_DIAMOND
5108
5109       // 0x042d - 0x0684: (?)
5110       // EL_EMERALD
5111
5112     case 0x06f1:
5113       element = EL_NUT;
5114       break;
5115
5116     case 0x074c:
5117       element = EL_BOMB;
5118       break;
5119
5120     case 0x07a4:
5121       element = EL_PEARL;
5122       break;
5123
5124     case 0x0823:
5125       element = EL_CRYSTAL;
5126       break;
5127
5128     case 0x0e77:        // quicksand (boulder)
5129       element = EL_QUICKSAND_FAST_FULL;
5130       break;
5131
5132     case 0x0e99:        // slow quicksand (boulder)
5133       element = EL_QUICKSAND_FULL;
5134       break;
5135
5136     case 0x0ed2:
5137       element = EL_EM_EXIT_OPEN;
5138       break;
5139
5140     case 0x0ee3:
5141       element = EL_EM_EXIT_CLOSED;
5142       break;
5143
5144     case 0x0eeb:
5145       element = EL_EM_STEEL_EXIT_OPEN;
5146       break;
5147
5148     case 0x0efc:
5149       element = EL_EM_STEEL_EXIT_CLOSED;
5150       break;
5151
5152     case 0x0f4f:        // dynamite (lit 1)
5153       element = EL_EM_DYNAMITE_ACTIVE;
5154       break;
5155
5156     case 0x0f57:        // dynamite (lit 2)
5157       element = EL_EM_DYNAMITE_ACTIVE;
5158       break;
5159
5160     case 0x0f5f:        // dynamite (lit 3)
5161       element = EL_EM_DYNAMITE_ACTIVE;
5162       break;
5163
5164     case 0x0f67:        // dynamite (lit 4)
5165       element = EL_EM_DYNAMITE_ACTIVE;
5166       break;
5167
5168     case 0x0f81:
5169     case 0x0f82:
5170     case 0x0f83:
5171     case 0x0f84:
5172       element = EL_AMOEBA_WET;
5173       break;
5174
5175     case 0x0f85:
5176       element = EL_AMOEBA_DROP;
5177       break;
5178
5179     case 0x0fb9:
5180       element = EL_DC_MAGIC_WALL;
5181       break;
5182
5183     case 0x0fd0:
5184       element = EL_SPACESHIP_UP;
5185       break;
5186
5187     case 0x0fd9:
5188       element = EL_SPACESHIP_DOWN;
5189       break;
5190
5191     case 0x0ff1:
5192       element = EL_SPACESHIP_LEFT;
5193       break;
5194
5195     case 0x0ff9:
5196       element = EL_SPACESHIP_RIGHT;
5197       break;
5198
5199     case 0x1057:
5200       element = EL_BUG_UP;
5201       break;
5202
5203     case 0x1060:
5204       element = EL_BUG_DOWN;
5205       break;
5206
5207     case 0x1078:
5208       element = EL_BUG_LEFT;
5209       break;
5210
5211     case 0x1080:
5212       element = EL_BUG_RIGHT;
5213       break;
5214
5215     case 0x10de:
5216       element = EL_MOLE_UP;
5217       break;
5218
5219     case 0x10e7:
5220       element = EL_MOLE_DOWN;
5221       break;
5222
5223     case 0x10ff:
5224       element = EL_MOLE_LEFT;
5225       break;
5226
5227     case 0x1107:
5228       element = EL_MOLE_RIGHT;
5229       break;
5230
5231     case 0x11c0:
5232       element = EL_ROBOT;
5233       break;
5234
5235     case 0x13f5:
5236       element = EL_YAMYAM_UP;
5237       break;
5238
5239     case 0x1425:
5240       element = EL_SWITCHGATE_OPEN;
5241       break;
5242
5243     case 0x1426:
5244       element = EL_SWITCHGATE_CLOSED;
5245       break;
5246
5247     case 0x1437:
5248       element = EL_DC_SWITCHGATE_SWITCH_UP;
5249       break;
5250
5251     case 0x143a:
5252       element = EL_TIMEGATE_CLOSED;
5253       break;
5254
5255     case 0x144c:        // conveyor belt switch (green)
5256       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5257       break;
5258
5259     case 0x144f:        // conveyor belt switch (red)
5260       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5261       break;
5262
5263     case 0x1452:        // conveyor belt switch (blue)
5264       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5265       break;
5266
5267     case 0x145b:
5268       element = EL_CONVEYOR_BELT_3_MIDDLE;
5269       break;
5270
5271     case 0x1463:
5272       element = EL_CONVEYOR_BELT_3_LEFT;
5273       break;
5274
5275     case 0x146b:
5276       element = EL_CONVEYOR_BELT_3_RIGHT;
5277       break;
5278
5279     case 0x1473:
5280       element = EL_CONVEYOR_BELT_1_MIDDLE;
5281       break;
5282
5283     case 0x147b:
5284       element = EL_CONVEYOR_BELT_1_LEFT;
5285       break;
5286
5287     case 0x1483:
5288       element = EL_CONVEYOR_BELT_1_RIGHT;
5289       break;
5290
5291     case 0x148b:
5292       element = EL_CONVEYOR_BELT_4_MIDDLE;
5293       break;
5294
5295     case 0x1493:
5296       element = EL_CONVEYOR_BELT_4_LEFT;
5297       break;
5298
5299     case 0x149b:
5300       element = EL_CONVEYOR_BELT_4_RIGHT;
5301       break;
5302
5303     case 0x14ac:
5304       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5305       break;
5306
5307     case 0x14bd:
5308       element = EL_EXPANDABLE_WALL_VERTICAL;
5309       break;
5310
5311     case 0x14c6:
5312       element = EL_EXPANDABLE_WALL_ANY;
5313       break;
5314
5315     case 0x14ce:        // growing steel wall (left/right)
5316       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5317       break;
5318
5319     case 0x14df:        // growing steel wall (up/down)
5320       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5321       break;
5322
5323     case 0x14e8:        // growing steel wall (up/down/left/right)
5324       element = EL_EXPANDABLE_STEELWALL_ANY;
5325       break;
5326
5327     case 0x14e9:
5328       element = EL_SHIELD_DEADLY;
5329       break;
5330
5331     case 0x1501:
5332       element = EL_EXTRA_TIME;
5333       break;
5334
5335     case 0x154f:
5336       element = EL_ACID;
5337       break;
5338
5339     case 0x1577:
5340       element = EL_EMPTY_SPACE;
5341       break;
5342
5343     case 0x1578:        // quicksand (empty)
5344       element = EL_QUICKSAND_FAST_EMPTY;
5345       break;
5346
5347     case 0x1579:        // slow quicksand (empty)
5348       element = EL_QUICKSAND_EMPTY;
5349       break;
5350
5351       // 0x157c - 0x158b:
5352       // EL_SAND
5353
5354       // 0x1590 - 0x159f:
5355       // EL_DC_LANDMINE
5356
5357     case 0x15a0:
5358       element = EL_EM_DYNAMITE;
5359       break;
5360
5361     case 0x15a1:        // key (red)
5362       element = EL_EM_KEY_1;
5363       break;
5364
5365     case 0x15a2:        // key (yellow)
5366       element = EL_EM_KEY_2;
5367       break;
5368
5369     case 0x15a3:        // key (blue)
5370       element = EL_EM_KEY_4;
5371       break;
5372
5373     case 0x15a4:        // key (green)
5374       element = EL_EM_KEY_3;
5375       break;
5376
5377     case 0x15a5:        // key (white)
5378       element = EL_DC_KEY_WHITE;
5379       break;
5380
5381     case 0x15a6:
5382       element = EL_WALL_SLIPPERY;
5383       break;
5384
5385     case 0x15a7:
5386       element = EL_WALL;
5387       break;
5388
5389     case 0x15a8:        // wall (not round)
5390       element = EL_WALL;
5391       break;
5392
5393     case 0x15a9:        // (blue)
5394       element = EL_CHAR_A;
5395       break;
5396
5397     case 0x15aa:        // (blue)
5398       element = EL_CHAR_B;
5399       break;
5400
5401     case 0x15ab:        // (blue)
5402       element = EL_CHAR_C;
5403       break;
5404
5405     case 0x15ac:        // (blue)
5406       element = EL_CHAR_D;
5407       break;
5408
5409     case 0x15ad:        // (blue)
5410       element = EL_CHAR_E;
5411       break;
5412
5413     case 0x15ae:        // (blue)
5414       element = EL_CHAR_F;
5415       break;
5416
5417     case 0x15af:        // (blue)
5418       element = EL_CHAR_G;
5419       break;
5420
5421     case 0x15b0:        // (blue)
5422       element = EL_CHAR_H;
5423       break;
5424
5425     case 0x15b1:        // (blue)
5426       element = EL_CHAR_I;
5427       break;
5428
5429     case 0x15b2:        // (blue)
5430       element = EL_CHAR_J;
5431       break;
5432
5433     case 0x15b3:        // (blue)
5434       element = EL_CHAR_K;
5435       break;
5436
5437     case 0x15b4:        // (blue)
5438       element = EL_CHAR_L;
5439       break;
5440
5441     case 0x15b5:        // (blue)
5442       element = EL_CHAR_M;
5443       break;
5444
5445     case 0x15b6:        // (blue)
5446       element = EL_CHAR_N;
5447       break;
5448
5449     case 0x15b7:        // (blue)
5450       element = EL_CHAR_O;
5451       break;
5452
5453     case 0x15b8:        // (blue)
5454       element = EL_CHAR_P;
5455       break;
5456
5457     case 0x15b9:        // (blue)
5458       element = EL_CHAR_Q;
5459       break;
5460
5461     case 0x15ba:        // (blue)
5462       element = EL_CHAR_R;
5463       break;
5464
5465     case 0x15bb:        // (blue)
5466       element = EL_CHAR_S;
5467       break;
5468
5469     case 0x15bc:        // (blue)
5470       element = EL_CHAR_T;
5471       break;
5472
5473     case 0x15bd:        // (blue)
5474       element = EL_CHAR_U;
5475       break;
5476
5477     case 0x15be:        // (blue)
5478       element = EL_CHAR_V;
5479       break;
5480
5481     case 0x15bf:        // (blue)
5482       element = EL_CHAR_W;
5483       break;
5484
5485     case 0x15c0:        // (blue)
5486       element = EL_CHAR_X;
5487       break;
5488
5489     case 0x15c1:        // (blue)
5490       element = EL_CHAR_Y;
5491       break;
5492
5493     case 0x15c2:        // (blue)
5494       element = EL_CHAR_Z;
5495       break;
5496
5497     case 0x15c3:        // (blue)
5498       element = EL_CHAR_AUMLAUT;
5499       break;
5500
5501     case 0x15c4:        // (blue)
5502       element = EL_CHAR_OUMLAUT;
5503       break;
5504
5505     case 0x15c5:        // (blue)
5506       element = EL_CHAR_UUMLAUT;
5507       break;
5508
5509     case 0x15c6:        // (blue)
5510       element = EL_CHAR_0;
5511       break;
5512
5513     case 0x15c7:        // (blue)
5514       element = EL_CHAR_1;
5515       break;
5516
5517     case 0x15c8:        // (blue)
5518       element = EL_CHAR_2;
5519       break;
5520
5521     case 0x15c9:        // (blue)
5522       element = EL_CHAR_3;
5523       break;
5524
5525     case 0x15ca:        // (blue)
5526       element = EL_CHAR_4;
5527       break;
5528
5529     case 0x15cb:        // (blue)
5530       element = EL_CHAR_5;
5531       break;
5532
5533     case 0x15cc:        // (blue)
5534       element = EL_CHAR_6;
5535       break;
5536
5537     case 0x15cd:        // (blue)
5538       element = EL_CHAR_7;
5539       break;
5540
5541     case 0x15ce:        // (blue)
5542       element = EL_CHAR_8;
5543       break;
5544
5545     case 0x15cf:        // (blue)
5546       element = EL_CHAR_9;
5547       break;
5548
5549     case 0x15d0:        // (blue)
5550       element = EL_CHAR_PERIOD;
5551       break;
5552
5553     case 0x15d1:        // (blue)
5554       element = EL_CHAR_EXCLAM;
5555       break;
5556
5557     case 0x15d2:        // (blue)
5558       element = EL_CHAR_COLON;
5559       break;
5560
5561     case 0x15d3:        // (blue)
5562       element = EL_CHAR_LESS;
5563       break;
5564
5565     case 0x15d4:        // (blue)
5566       element = EL_CHAR_GREATER;
5567       break;
5568
5569     case 0x15d5:        // (blue)
5570       element = EL_CHAR_QUESTION;
5571       break;
5572
5573     case 0x15d6:        // (blue)
5574       element = EL_CHAR_COPYRIGHT;
5575       break;
5576
5577     case 0x15d7:        // (blue)
5578       element = EL_CHAR_UP;
5579       break;
5580
5581     case 0x15d8:        // (blue)
5582       element = EL_CHAR_DOWN;
5583       break;
5584
5585     case 0x15d9:        // (blue)
5586       element = EL_CHAR_BUTTON;
5587       break;
5588
5589     case 0x15da:        // (blue)
5590       element = EL_CHAR_PLUS;
5591       break;
5592
5593     case 0x15db:        // (blue)
5594       element = EL_CHAR_MINUS;
5595       break;
5596
5597     case 0x15dc:        // (blue)
5598       element = EL_CHAR_APOSTROPHE;
5599       break;
5600
5601     case 0x15dd:        // (blue)
5602       element = EL_CHAR_PARENLEFT;
5603       break;
5604
5605     case 0x15de:        // (blue)
5606       element = EL_CHAR_PARENRIGHT;
5607       break;
5608
5609     case 0x15df:        // (green)
5610       element = EL_CHAR_A;
5611       break;
5612
5613     case 0x15e0:        // (green)
5614       element = EL_CHAR_B;
5615       break;
5616
5617     case 0x15e1:        // (green)
5618       element = EL_CHAR_C;
5619       break;
5620
5621     case 0x15e2:        // (green)
5622       element = EL_CHAR_D;
5623       break;
5624
5625     case 0x15e3:        // (green)
5626       element = EL_CHAR_E;
5627       break;
5628
5629     case 0x15e4:        // (green)
5630       element = EL_CHAR_F;
5631       break;
5632
5633     case 0x15e5:        // (green)
5634       element = EL_CHAR_G;
5635       break;
5636
5637     case 0x15e6:        // (green)
5638       element = EL_CHAR_H;
5639       break;
5640
5641     case 0x15e7:        // (green)
5642       element = EL_CHAR_I;
5643       break;
5644
5645     case 0x15e8:        // (green)
5646       element = EL_CHAR_J;
5647       break;
5648
5649     case 0x15e9:        // (green)
5650       element = EL_CHAR_K;
5651       break;
5652
5653     case 0x15ea:        // (green)
5654       element = EL_CHAR_L;
5655       break;
5656
5657     case 0x15eb:        // (green)
5658       element = EL_CHAR_M;
5659       break;
5660
5661     case 0x15ec:        // (green)
5662       element = EL_CHAR_N;
5663       break;
5664
5665     case 0x15ed:        // (green)
5666       element = EL_CHAR_O;
5667       break;
5668
5669     case 0x15ee:        // (green)
5670       element = EL_CHAR_P;
5671       break;
5672
5673     case 0x15ef:        // (green)
5674       element = EL_CHAR_Q;
5675       break;
5676
5677     case 0x15f0:        // (green)
5678       element = EL_CHAR_R;
5679       break;
5680
5681     case 0x15f1:        // (green)
5682       element = EL_CHAR_S;
5683       break;
5684
5685     case 0x15f2:        // (green)
5686       element = EL_CHAR_T;
5687       break;
5688
5689     case 0x15f3:        // (green)
5690       element = EL_CHAR_U;
5691       break;
5692
5693     case 0x15f4:        // (green)
5694       element = EL_CHAR_V;
5695       break;
5696
5697     case 0x15f5:        // (green)
5698       element = EL_CHAR_W;
5699       break;
5700
5701     case 0x15f6:        // (green)
5702       element = EL_CHAR_X;
5703       break;
5704
5705     case 0x15f7:        // (green)
5706       element = EL_CHAR_Y;
5707       break;
5708
5709     case 0x15f8:        // (green)
5710       element = EL_CHAR_Z;
5711       break;
5712
5713     case 0x15f9:        // (green)
5714       element = EL_CHAR_AUMLAUT;
5715       break;
5716
5717     case 0x15fa:        // (green)
5718       element = EL_CHAR_OUMLAUT;
5719       break;
5720
5721     case 0x15fb:        // (green)
5722       element = EL_CHAR_UUMLAUT;
5723       break;
5724
5725     case 0x15fc:        // (green)
5726       element = EL_CHAR_0;
5727       break;
5728
5729     case 0x15fd:        // (green)
5730       element = EL_CHAR_1;
5731       break;
5732
5733     case 0x15fe:        // (green)
5734       element = EL_CHAR_2;
5735       break;
5736
5737     case 0x15ff:        // (green)
5738       element = EL_CHAR_3;
5739       break;
5740
5741     case 0x1600:        // (green)
5742       element = EL_CHAR_4;
5743       break;
5744
5745     case 0x1601:        // (green)
5746       element = EL_CHAR_5;
5747       break;
5748
5749     case 0x1602:        // (green)
5750       element = EL_CHAR_6;
5751       break;
5752
5753     case 0x1603:        // (green)
5754       element = EL_CHAR_7;
5755       break;
5756
5757     case 0x1604:        // (green)
5758       element = EL_CHAR_8;
5759       break;
5760
5761     case 0x1605:        // (green)
5762       element = EL_CHAR_9;
5763       break;
5764
5765     case 0x1606:        // (green)
5766       element = EL_CHAR_PERIOD;
5767       break;
5768
5769     case 0x1607:        // (green)
5770       element = EL_CHAR_EXCLAM;
5771       break;
5772
5773     case 0x1608:        // (green)
5774       element = EL_CHAR_COLON;
5775       break;
5776
5777     case 0x1609:        // (green)
5778       element = EL_CHAR_LESS;
5779       break;
5780
5781     case 0x160a:        // (green)
5782       element = EL_CHAR_GREATER;
5783       break;
5784
5785     case 0x160b:        // (green)
5786       element = EL_CHAR_QUESTION;
5787       break;
5788
5789     case 0x160c:        // (green)
5790       element = EL_CHAR_COPYRIGHT;
5791       break;
5792
5793     case 0x160d:        // (green)
5794       element = EL_CHAR_UP;
5795       break;
5796
5797     case 0x160e:        // (green)
5798       element = EL_CHAR_DOWN;
5799       break;
5800
5801     case 0x160f:        // (green)
5802       element = EL_CHAR_BUTTON;
5803       break;
5804
5805     case 0x1610:        // (green)
5806       element = EL_CHAR_PLUS;
5807       break;
5808
5809     case 0x1611:        // (green)
5810       element = EL_CHAR_MINUS;
5811       break;
5812
5813     case 0x1612:        // (green)
5814       element = EL_CHAR_APOSTROPHE;
5815       break;
5816
5817     case 0x1613:        // (green)
5818       element = EL_CHAR_PARENLEFT;
5819       break;
5820
5821     case 0x1614:        // (green)
5822       element = EL_CHAR_PARENRIGHT;
5823       break;
5824
5825     case 0x1615:        // (blue steel)
5826       element = EL_STEEL_CHAR_A;
5827       break;
5828
5829     case 0x1616:        // (blue steel)
5830       element = EL_STEEL_CHAR_B;
5831       break;
5832
5833     case 0x1617:        // (blue steel)
5834       element = EL_STEEL_CHAR_C;
5835       break;
5836
5837     case 0x1618:        // (blue steel)
5838       element = EL_STEEL_CHAR_D;
5839       break;
5840
5841     case 0x1619:        // (blue steel)
5842       element = EL_STEEL_CHAR_E;
5843       break;
5844
5845     case 0x161a:        // (blue steel)
5846       element = EL_STEEL_CHAR_F;
5847       break;
5848
5849     case 0x161b:        // (blue steel)
5850       element = EL_STEEL_CHAR_G;
5851       break;
5852
5853     case 0x161c:        // (blue steel)
5854       element = EL_STEEL_CHAR_H;
5855       break;
5856
5857     case 0x161d:        // (blue steel)
5858       element = EL_STEEL_CHAR_I;
5859       break;
5860
5861     case 0x161e:        // (blue steel)
5862       element = EL_STEEL_CHAR_J;
5863       break;
5864
5865     case 0x161f:        // (blue steel)
5866       element = EL_STEEL_CHAR_K;
5867       break;
5868
5869     case 0x1620:        // (blue steel)
5870       element = EL_STEEL_CHAR_L;
5871       break;
5872
5873     case 0x1621:        // (blue steel)
5874       element = EL_STEEL_CHAR_M;
5875       break;
5876
5877     case 0x1622:        // (blue steel)
5878       element = EL_STEEL_CHAR_N;
5879       break;
5880
5881     case 0x1623:        // (blue steel)
5882       element = EL_STEEL_CHAR_O;
5883       break;
5884
5885     case 0x1624:        // (blue steel)
5886       element = EL_STEEL_CHAR_P;
5887       break;
5888
5889     case 0x1625:        // (blue steel)
5890       element = EL_STEEL_CHAR_Q;
5891       break;
5892
5893     case 0x1626:        // (blue steel)
5894       element = EL_STEEL_CHAR_R;
5895       break;
5896
5897     case 0x1627:        // (blue steel)
5898       element = EL_STEEL_CHAR_S;
5899       break;
5900
5901     case 0x1628:        // (blue steel)
5902       element = EL_STEEL_CHAR_T;
5903       break;
5904
5905     case 0x1629:        // (blue steel)
5906       element = EL_STEEL_CHAR_U;
5907       break;
5908
5909     case 0x162a:        // (blue steel)
5910       element = EL_STEEL_CHAR_V;
5911       break;
5912
5913     case 0x162b:        // (blue steel)
5914       element = EL_STEEL_CHAR_W;
5915       break;
5916
5917     case 0x162c:        // (blue steel)
5918       element = EL_STEEL_CHAR_X;
5919       break;
5920
5921     case 0x162d:        // (blue steel)
5922       element = EL_STEEL_CHAR_Y;
5923       break;
5924
5925     case 0x162e:        // (blue steel)
5926       element = EL_STEEL_CHAR_Z;
5927       break;
5928
5929     case 0x162f:        // (blue steel)
5930       element = EL_STEEL_CHAR_AUMLAUT;
5931       break;
5932
5933     case 0x1630:        // (blue steel)
5934       element = EL_STEEL_CHAR_OUMLAUT;
5935       break;
5936
5937     case 0x1631:        // (blue steel)
5938       element = EL_STEEL_CHAR_UUMLAUT;
5939       break;
5940
5941     case 0x1632:        // (blue steel)
5942       element = EL_STEEL_CHAR_0;
5943       break;
5944
5945     case 0x1633:        // (blue steel)
5946       element = EL_STEEL_CHAR_1;
5947       break;
5948
5949     case 0x1634:        // (blue steel)
5950       element = EL_STEEL_CHAR_2;
5951       break;
5952
5953     case 0x1635:        // (blue steel)
5954       element = EL_STEEL_CHAR_3;
5955       break;
5956
5957     case 0x1636:        // (blue steel)
5958       element = EL_STEEL_CHAR_4;
5959       break;
5960
5961     case 0x1637:        // (blue steel)
5962       element = EL_STEEL_CHAR_5;
5963       break;
5964
5965     case 0x1638:        // (blue steel)
5966       element = EL_STEEL_CHAR_6;
5967       break;
5968
5969     case 0x1639:        // (blue steel)
5970       element = EL_STEEL_CHAR_7;
5971       break;
5972
5973     case 0x163a:        // (blue steel)
5974       element = EL_STEEL_CHAR_8;
5975       break;
5976
5977     case 0x163b:        // (blue steel)
5978       element = EL_STEEL_CHAR_9;
5979       break;
5980
5981     case 0x163c:        // (blue steel)
5982       element = EL_STEEL_CHAR_PERIOD;
5983       break;
5984
5985     case 0x163d:        // (blue steel)
5986       element = EL_STEEL_CHAR_EXCLAM;
5987       break;
5988
5989     case 0x163e:        // (blue steel)
5990       element = EL_STEEL_CHAR_COLON;
5991       break;
5992
5993     case 0x163f:        // (blue steel)
5994       element = EL_STEEL_CHAR_LESS;
5995       break;
5996
5997     case 0x1640:        // (blue steel)
5998       element = EL_STEEL_CHAR_GREATER;
5999       break;
6000
6001     case 0x1641:        // (blue steel)
6002       element = EL_STEEL_CHAR_QUESTION;
6003       break;
6004
6005     case 0x1642:        // (blue steel)
6006       element = EL_STEEL_CHAR_COPYRIGHT;
6007       break;
6008
6009     case 0x1643:        // (blue steel)
6010       element = EL_STEEL_CHAR_UP;
6011       break;
6012
6013     case 0x1644:        // (blue steel)
6014       element = EL_STEEL_CHAR_DOWN;
6015       break;
6016
6017     case 0x1645:        // (blue steel)
6018       element = EL_STEEL_CHAR_BUTTON;
6019       break;
6020
6021     case 0x1646:        // (blue steel)
6022       element = EL_STEEL_CHAR_PLUS;
6023       break;
6024
6025     case 0x1647:        // (blue steel)
6026       element = EL_STEEL_CHAR_MINUS;
6027       break;
6028
6029     case 0x1648:        // (blue steel)
6030       element = EL_STEEL_CHAR_APOSTROPHE;
6031       break;
6032
6033     case 0x1649:        // (blue steel)
6034       element = EL_STEEL_CHAR_PARENLEFT;
6035       break;
6036
6037     case 0x164a:        // (blue steel)
6038       element = EL_STEEL_CHAR_PARENRIGHT;
6039       break;
6040
6041     case 0x164b:        // (green steel)
6042       element = EL_STEEL_CHAR_A;
6043       break;
6044
6045     case 0x164c:        // (green steel)
6046       element = EL_STEEL_CHAR_B;
6047       break;
6048
6049     case 0x164d:        // (green steel)
6050       element = EL_STEEL_CHAR_C;
6051       break;
6052
6053     case 0x164e:        // (green steel)
6054       element = EL_STEEL_CHAR_D;
6055       break;
6056
6057     case 0x164f:        // (green steel)
6058       element = EL_STEEL_CHAR_E;
6059       break;
6060
6061     case 0x1650:        // (green steel)
6062       element = EL_STEEL_CHAR_F;
6063       break;
6064
6065     case 0x1651:        // (green steel)
6066       element = EL_STEEL_CHAR_G;
6067       break;
6068
6069     case 0x1652:        // (green steel)
6070       element = EL_STEEL_CHAR_H;
6071       break;
6072
6073     case 0x1653:        // (green steel)
6074       element = EL_STEEL_CHAR_I;
6075       break;
6076
6077     case 0x1654:        // (green steel)
6078       element = EL_STEEL_CHAR_J;
6079       break;
6080
6081     case 0x1655:        // (green steel)
6082       element = EL_STEEL_CHAR_K;
6083       break;
6084
6085     case 0x1656:        // (green steel)
6086       element = EL_STEEL_CHAR_L;
6087       break;
6088
6089     case 0x1657:        // (green steel)
6090       element = EL_STEEL_CHAR_M;
6091       break;
6092
6093     case 0x1658:        // (green steel)
6094       element = EL_STEEL_CHAR_N;
6095       break;
6096
6097     case 0x1659:        // (green steel)
6098       element = EL_STEEL_CHAR_O;
6099       break;
6100
6101     case 0x165a:        // (green steel)
6102       element = EL_STEEL_CHAR_P;
6103       break;
6104
6105     case 0x165b:        // (green steel)
6106       element = EL_STEEL_CHAR_Q;
6107       break;
6108
6109     case 0x165c:        // (green steel)
6110       element = EL_STEEL_CHAR_R;
6111       break;
6112
6113     case 0x165d:        // (green steel)
6114       element = EL_STEEL_CHAR_S;
6115       break;
6116
6117     case 0x165e:        // (green steel)
6118       element = EL_STEEL_CHAR_T;
6119       break;
6120
6121     case 0x165f:        // (green steel)
6122       element = EL_STEEL_CHAR_U;
6123       break;
6124
6125     case 0x1660:        // (green steel)
6126       element = EL_STEEL_CHAR_V;
6127       break;
6128
6129     case 0x1661:        // (green steel)
6130       element = EL_STEEL_CHAR_W;
6131       break;
6132
6133     case 0x1662:        // (green steel)
6134       element = EL_STEEL_CHAR_X;
6135       break;
6136
6137     case 0x1663:        // (green steel)
6138       element = EL_STEEL_CHAR_Y;
6139       break;
6140
6141     case 0x1664:        // (green steel)
6142       element = EL_STEEL_CHAR_Z;
6143       break;
6144
6145     case 0x1665:        // (green steel)
6146       element = EL_STEEL_CHAR_AUMLAUT;
6147       break;
6148
6149     case 0x1666:        // (green steel)
6150       element = EL_STEEL_CHAR_OUMLAUT;
6151       break;
6152
6153     case 0x1667:        // (green steel)
6154       element = EL_STEEL_CHAR_UUMLAUT;
6155       break;
6156
6157     case 0x1668:        // (green steel)
6158       element = EL_STEEL_CHAR_0;
6159       break;
6160
6161     case 0x1669:        // (green steel)
6162       element = EL_STEEL_CHAR_1;
6163       break;
6164
6165     case 0x166a:        // (green steel)
6166       element = EL_STEEL_CHAR_2;
6167       break;
6168
6169     case 0x166b:        // (green steel)
6170       element = EL_STEEL_CHAR_3;
6171       break;
6172
6173     case 0x166c:        // (green steel)
6174       element = EL_STEEL_CHAR_4;
6175       break;
6176
6177     case 0x166d:        // (green steel)
6178       element = EL_STEEL_CHAR_5;
6179       break;
6180
6181     case 0x166e:        // (green steel)
6182       element = EL_STEEL_CHAR_6;
6183       break;
6184
6185     case 0x166f:        // (green steel)
6186       element = EL_STEEL_CHAR_7;
6187       break;
6188
6189     case 0x1670:        // (green steel)
6190       element = EL_STEEL_CHAR_8;
6191       break;
6192
6193     case 0x1671:        // (green steel)
6194       element = EL_STEEL_CHAR_9;
6195       break;
6196
6197     case 0x1672:        // (green steel)
6198       element = EL_STEEL_CHAR_PERIOD;
6199       break;
6200
6201     case 0x1673:        // (green steel)
6202       element = EL_STEEL_CHAR_EXCLAM;
6203       break;
6204
6205     case 0x1674:        // (green steel)
6206       element = EL_STEEL_CHAR_COLON;
6207       break;
6208
6209     case 0x1675:        // (green steel)
6210       element = EL_STEEL_CHAR_LESS;
6211       break;
6212
6213     case 0x1676:        // (green steel)
6214       element = EL_STEEL_CHAR_GREATER;
6215       break;
6216
6217     case 0x1677:        // (green steel)
6218       element = EL_STEEL_CHAR_QUESTION;
6219       break;
6220
6221     case 0x1678:        // (green steel)
6222       element = EL_STEEL_CHAR_COPYRIGHT;
6223       break;
6224
6225     case 0x1679:        // (green steel)
6226       element = EL_STEEL_CHAR_UP;
6227       break;
6228
6229     case 0x167a:        // (green steel)
6230       element = EL_STEEL_CHAR_DOWN;
6231       break;
6232
6233     case 0x167b:        // (green steel)
6234       element = EL_STEEL_CHAR_BUTTON;
6235       break;
6236
6237     case 0x167c:        // (green steel)
6238       element = EL_STEEL_CHAR_PLUS;
6239       break;
6240
6241     case 0x167d:        // (green steel)
6242       element = EL_STEEL_CHAR_MINUS;
6243       break;
6244
6245     case 0x167e:        // (green steel)
6246       element = EL_STEEL_CHAR_APOSTROPHE;
6247       break;
6248
6249     case 0x167f:        // (green steel)
6250       element = EL_STEEL_CHAR_PARENLEFT;
6251       break;
6252
6253     case 0x1680:        // (green steel)
6254       element = EL_STEEL_CHAR_PARENRIGHT;
6255       break;
6256
6257     case 0x1681:        // gate (red)
6258       element = EL_EM_GATE_1;
6259       break;
6260
6261     case 0x1682:        // secret gate (red)
6262       element = EL_EM_GATE_1_GRAY;
6263       break;
6264
6265     case 0x1683:        // gate (yellow)
6266       element = EL_EM_GATE_2;
6267       break;
6268
6269     case 0x1684:        // secret gate (yellow)
6270       element = EL_EM_GATE_2_GRAY;
6271       break;
6272
6273     case 0x1685:        // gate (blue)
6274       element = EL_EM_GATE_4;
6275       break;
6276
6277     case 0x1686:        // secret gate (blue)
6278       element = EL_EM_GATE_4_GRAY;
6279       break;
6280
6281     case 0x1687:        // gate (green)
6282       element = EL_EM_GATE_3;
6283       break;
6284
6285     case 0x1688:        // secret gate (green)
6286       element = EL_EM_GATE_3_GRAY;
6287       break;
6288
6289     case 0x1689:        // gate (white)
6290       element = EL_DC_GATE_WHITE;
6291       break;
6292
6293     case 0x168a:        // secret gate (white)
6294       element = EL_DC_GATE_WHITE_GRAY;
6295       break;
6296
6297     case 0x168b:        // secret gate (no key)
6298       element = EL_DC_GATE_FAKE_GRAY;
6299       break;
6300
6301     case 0x168c:
6302       element = EL_ROBOT_WHEEL;
6303       break;
6304
6305     case 0x168d:
6306       element = EL_DC_TIMEGATE_SWITCH;
6307       break;
6308
6309     case 0x168e:
6310       element = EL_ACID_POOL_BOTTOM;
6311       break;
6312
6313     case 0x168f:
6314       element = EL_ACID_POOL_TOPLEFT;
6315       break;
6316
6317     case 0x1690:
6318       element = EL_ACID_POOL_TOPRIGHT;
6319       break;
6320
6321     case 0x1691:
6322       element = EL_ACID_POOL_BOTTOMLEFT;
6323       break;
6324
6325     case 0x1692:
6326       element = EL_ACID_POOL_BOTTOMRIGHT;
6327       break;
6328
6329     case 0x1693:
6330       element = EL_STEELWALL;
6331       break;
6332
6333     case 0x1694:
6334       element = EL_STEELWALL_SLIPPERY;
6335       break;
6336
6337     case 0x1695:        // steel wall (not round)
6338       element = EL_STEELWALL;
6339       break;
6340
6341     case 0x1696:        // steel wall (left)
6342       element = EL_DC_STEELWALL_1_LEFT;
6343       break;
6344
6345     case 0x1697:        // steel wall (bottom)
6346       element = EL_DC_STEELWALL_1_BOTTOM;
6347       break;
6348
6349     case 0x1698:        // steel wall (right)
6350       element = EL_DC_STEELWALL_1_RIGHT;
6351       break;
6352
6353     case 0x1699:        // steel wall (top)
6354       element = EL_DC_STEELWALL_1_TOP;
6355       break;
6356
6357     case 0x169a:        // steel wall (left/bottom)
6358       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6359       break;
6360
6361     case 0x169b:        // steel wall (right/bottom)
6362       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6363       break;
6364
6365     case 0x169c:        // steel wall (right/top)
6366       element = EL_DC_STEELWALL_1_TOPRIGHT;
6367       break;
6368
6369     case 0x169d:        // steel wall (left/top)
6370       element = EL_DC_STEELWALL_1_TOPLEFT;
6371       break;
6372
6373     case 0x169e:        // steel wall (right/bottom small)
6374       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6375       break;
6376
6377     case 0x169f:        // steel wall (left/bottom small)
6378       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6379       break;
6380
6381     case 0x16a0:        // steel wall (right/top small)
6382       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6383       break;
6384
6385     case 0x16a1:        // steel wall (left/top small)
6386       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6387       break;
6388
6389     case 0x16a2:        // steel wall (left/right)
6390       element = EL_DC_STEELWALL_1_VERTICAL;
6391       break;
6392
6393     case 0x16a3:        // steel wall (top/bottom)
6394       element = EL_DC_STEELWALL_1_HORIZONTAL;
6395       break;
6396
6397     case 0x16a4:        // steel wall 2 (left end)
6398       element = EL_DC_STEELWALL_2_LEFT;
6399       break;
6400
6401     case 0x16a5:        // steel wall 2 (right end)
6402       element = EL_DC_STEELWALL_2_RIGHT;
6403       break;
6404
6405     case 0x16a6:        // steel wall 2 (top end)
6406       element = EL_DC_STEELWALL_2_TOP;
6407       break;
6408
6409     case 0x16a7:        // steel wall 2 (bottom end)
6410       element = EL_DC_STEELWALL_2_BOTTOM;
6411       break;
6412
6413     case 0x16a8:        // steel wall 2 (left/right)
6414       element = EL_DC_STEELWALL_2_HORIZONTAL;
6415       break;
6416
6417     case 0x16a9:        // steel wall 2 (up/down)
6418       element = EL_DC_STEELWALL_2_VERTICAL;
6419       break;
6420
6421     case 0x16aa:        // steel wall 2 (mid)
6422       element = EL_DC_STEELWALL_2_MIDDLE;
6423       break;
6424
6425     case 0x16ab:
6426       element = EL_SIGN_EXCLAMATION;
6427       break;
6428
6429     case 0x16ac:
6430       element = EL_SIGN_RADIOACTIVITY;
6431       break;
6432
6433     case 0x16ad:
6434       element = EL_SIGN_STOP;
6435       break;
6436
6437     case 0x16ae:
6438       element = EL_SIGN_WHEELCHAIR;
6439       break;
6440
6441     case 0x16af:
6442       element = EL_SIGN_PARKING;
6443       break;
6444
6445     case 0x16b0:
6446       element = EL_SIGN_NO_ENTRY;
6447       break;
6448
6449     case 0x16b1:
6450       element = EL_SIGN_HEART;
6451       break;
6452
6453     case 0x16b2:
6454       element = EL_SIGN_GIVE_WAY;
6455       break;
6456
6457     case 0x16b3:
6458       element = EL_SIGN_ENTRY_FORBIDDEN;
6459       break;
6460
6461     case 0x16b4:
6462       element = EL_SIGN_EMERGENCY_EXIT;
6463       break;
6464
6465     case 0x16b5:
6466       element = EL_SIGN_YIN_YANG;
6467       break;
6468
6469     case 0x16b6:
6470       element = EL_WALL_EMERALD;
6471       break;
6472
6473     case 0x16b7:
6474       element = EL_WALL_DIAMOND;
6475       break;
6476
6477     case 0x16b8:
6478       element = EL_WALL_PEARL;
6479       break;
6480
6481     case 0x16b9:
6482       element = EL_WALL_CRYSTAL;
6483       break;
6484
6485     case 0x16ba:
6486       element = EL_INVISIBLE_WALL;
6487       break;
6488
6489     case 0x16bb:
6490       element = EL_INVISIBLE_STEELWALL;
6491       break;
6492
6493       // 0x16bc - 0x16cb:
6494       // EL_INVISIBLE_SAND
6495
6496     case 0x16cc:
6497       element = EL_LIGHT_SWITCH;
6498       break;
6499
6500     case 0x16cd:
6501       element = EL_ENVELOPE_1;
6502       break;
6503
6504     default:
6505       if (element >= 0x0117 && element <= 0x036e)       // (?)
6506         element = EL_DIAMOND;
6507       else if (element >= 0x042d && element <= 0x0684)  // (?)
6508         element = EL_EMERALD;
6509       else if (element >= 0x157c && element <= 0x158b)
6510         element = EL_SAND;
6511       else if (element >= 0x1590 && element <= 0x159f)
6512         element = EL_DC_LANDMINE;
6513       else if (element >= 0x16bc && element <= 0x16cb)
6514         element = EL_INVISIBLE_SAND;
6515       else
6516       {
6517         Warn("unknown Diamond Caves element 0x%04x", element);
6518
6519         element = EL_UNKNOWN;
6520       }
6521       break;
6522   }
6523
6524   return getMappedElement(element);
6525 }
6526
6527 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6528 {
6529   byte header[DC_LEVEL_HEADER_SIZE];
6530   int envelope_size;
6531   int envelope_header_pos = 62;
6532   int envelope_content_pos = 94;
6533   int level_name_pos = 251;
6534   int level_author_pos = 292;
6535   int envelope_header_len;
6536   int envelope_content_len;
6537   int level_name_len;
6538   int level_author_len;
6539   int fieldx, fieldy;
6540   int num_yamyam_contents;
6541   int i, x, y;
6542
6543   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6544
6545   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6546   {
6547     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6548
6549     header[i * 2 + 0] = header_word >> 8;
6550     header[i * 2 + 1] = header_word & 0xff;
6551   }
6552
6553   // read some values from level header to check level decoding integrity
6554   fieldx = header[6] | (header[7] << 8);
6555   fieldy = header[8] | (header[9] << 8);
6556   num_yamyam_contents = header[60] | (header[61] << 8);
6557
6558   // do some simple sanity checks to ensure that level was correctly decoded
6559   if (fieldx < 1 || fieldx > 256 ||
6560       fieldy < 1 || fieldy > 256 ||
6561       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6562   {
6563     level->no_valid_file = TRUE;
6564
6565     Warn("cannot decode level from stream -- using empty level");
6566
6567     return;
6568   }
6569
6570   // maximum envelope header size is 31 bytes
6571   envelope_header_len   = header[envelope_header_pos];
6572   // maximum envelope content size is 110 (156?) bytes
6573   envelope_content_len  = header[envelope_content_pos];
6574
6575   // maximum level title size is 40 bytes
6576   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6577   // maximum level author size is 30 (51?) bytes
6578   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6579
6580   envelope_size = 0;
6581
6582   for (i = 0; i < envelope_header_len; i++)
6583     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6584       level->envelope[0].text[envelope_size++] =
6585         header[envelope_header_pos + 1 + i];
6586
6587   if (envelope_header_len > 0 && envelope_content_len > 0)
6588   {
6589     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6590       level->envelope[0].text[envelope_size++] = '\n';
6591     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6592       level->envelope[0].text[envelope_size++] = '\n';
6593   }
6594
6595   for (i = 0; i < envelope_content_len; i++)
6596     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6597       level->envelope[0].text[envelope_size++] =
6598         header[envelope_content_pos + 1 + i];
6599
6600   level->envelope[0].text[envelope_size] = '\0';
6601
6602   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6603   level->envelope[0].ysize = 10;
6604   level->envelope[0].autowrap = TRUE;
6605   level->envelope[0].centered = TRUE;
6606
6607   for (i = 0; i < level_name_len; i++)
6608     level->name[i] = header[level_name_pos + 1 + i];
6609   level->name[level_name_len] = '\0';
6610
6611   for (i = 0; i < level_author_len; i++)
6612     level->author[i] = header[level_author_pos + 1 + i];
6613   level->author[level_author_len] = '\0';
6614
6615   num_yamyam_contents = header[60] | (header[61] << 8);
6616   level->num_yamyam_contents =
6617     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6618
6619   for (i = 0; i < num_yamyam_contents; i++)
6620   {
6621     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6622     {
6623       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6624       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6625
6626       if (i < MAX_ELEMENT_CONTENTS)
6627         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6628     }
6629   }
6630
6631   fieldx = header[6] | (header[7] << 8);
6632   fieldy = header[8] | (header[9] << 8);
6633   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6634   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6635
6636   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6637   {
6638     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6639     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6640
6641     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6642       level->field[x][y] = getMappedElement_DC(element_dc);
6643   }
6644
6645   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6646   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6647   level->field[x][y] = EL_PLAYER_1;
6648
6649   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6650   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6651   level->field[x][y] = EL_PLAYER_2;
6652
6653   level->gems_needed            = header[18] | (header[19] << 8);
6654
6655   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6656   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6657   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6658   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6659   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6660   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6661   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6662   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6663   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6664   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6665   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6666   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6667
6668   level->time                   = header[44] | (header[45] << 8);
6669
6670   level->amoeba_speed           = header[46] | (header[47] << 8);
6671   level->time_light             = header[48] | (header[49] << 8);
6672   level->time_timegate          = header[50] | (header[51] << 8);
6673   level->time_wheel             = header[52] | (header[53] << 8);
6674   level->time_magic_wall        = header[54] | (header[55] << 8);
6675   level->extra_time             = header[56] | (header[57] << 8);
6676   level->shield_normal_time     = header[58] | (header[59] << 8);
6677
6678   // shield and extra time elements do not have a score
6679   level->score[SC_SHIELD]       = 0;
6680   level->extra_time_score       = 0;
6681
6682   // set time for normal and deadly shields to the same value
6683   level->shield_deadly_time     = level->shield_normal_time;
6684
6685   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6686   // can slip down from flat walls, like normal walls and steel walls
6687   level->em_slippery_gems = TRUE;
6688
6689   // time score is counted for each 10 seconds left in Diamond Caves levels
6690   level->time_score_base = 10;
6691 }
6692
6693 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6694                                      struct LevelFileInfo *level_file_info,
6695                                      boolean level_info_only)
6696 {
6697   char *filename = level_file_info->filename;
6698   File *file;
6699   int num_magic_bytes = 8;
6700   char magic_bytes[num_magic_bytes + 1];
6701   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6702
6703   if (!(file = openFile(filename, MODE_READ)))
6704   {
6705     level->no_valid_file = TRUE;
6706
6707     if (!level_info_only)
6708       Warn("cannot read level '%s' -- using empty level", filename);
6709
6710     return;
6711   }
6712
6713   // fseek(file, 0x0000, SEEK_SET);
6714
6715   if (level_file_info->packed)
6716   {
6717     // read "magic bytes" from start of file
6718     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6719       magic_bytes[0] = '\0';
6720
6721     // check "magic bytes" for correct file format
6722     if (!strPrefix(magic_bytes, "DC2"))
6723     {
6724       level->no_valid_file = TRUE;
6725
6726       Warn("unknown DC level file '%s' -- using empty level", filename);
6727
6728       return;
6729     }
6730
6731     if (strPrefix(magic_bytes, "DC2Win95") ||
6732         strPrefix(magic_bytes, "DC2Win98"))
6733     {
6734       int position_first_level = 0x00fa;
6735       int extra_bytes = 4;
6736       int skip_bytes;
6737
6738       // advance file stream to first level inside the level package
6739       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6740
6741       // each block of level data is followed by block of non-level data
6742       num_levels_to_skip *= 2;
6743
6744       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6745       while (num_levels_to_skip >= 0)
6746       {
6747         // advance file stream to next level inside the level package
6748         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6749         {
6750           level->no_valid_file = TRUE;
6751
6752           Warn("cannot fseek in file '%s' -- using empty level", filename);
6753
6754           return;
6755         }
6756
6757         // skip apparently unused extra bytes following each level
6758         ReadUnusedBytesFromFile(file, extra_bytes);
6759
6760         // read size of next level in level package
6761         skip_bytes = getFile32BitLE(file);
6762
6763         num_levels_to_skip--;
6764       }
6765     }
6766     else
6767     {
6768       level->no_valid_file = TRUE;
6769
6770       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6771
6772       return;
6773     }
6774   }
6775
6776   LoadLevelFromFileStream_DC(file, level);
6777
6778   closeFile(file);
6779 }
6780
6781
6782 // ----------------------------------------------------------------------------
6783 // functions for loading SB level
6784 // ----------------------------------------------------------------------------
6785
6786 int getMappedElement_SB(int element_ascii, boolean use_ces)
6787 {
6788   static struct
6789   {
6790     int ascii;
6791     int sb;
6792     int ce;
6793   }
6794   sb_element_mapping[] =
6795   {
6796     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6797     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6798     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6799     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6800     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6801     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6802     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6803     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6804
6805     { 0,   -1,                      -1          },
6806   };
6807
6808   int i;
6809
6810   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6811     if (element_ascii == sb_element_mapping[i].ascii)
6812       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6813
6814   return EL_UNDEFINED;
6815 }
6816
6817 static void SetLevelSettings_SB(struct LevelInfo *level)
6818 {
6819   // time settings
6820   level->time = 0;
6821   level->use_step_counter = TRUE;
6822
6823   // score settings
6824   level->score[SC_TIME_BONUS] = 0;
6825   level->time_score_base = 1;
6826   level->rate_time_over_score = TRUE;
6827
6828   // game settings
6829   level->auto_exit_sokoban = TRUE;
6830 }
6831
6832 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6833                                      struct LevelFileInfo *level_file_info,
6834                                      boolean level_info_only)
6835 {
6836   char *filename = level_file_info->filename;
6837   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6838   char last_comment[MAX_LINE_LEN];
6839   char level_name[MAX_LINE_LEN];
6840   char *line_ptr;
6841   File *file;
6842   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6843   boolean read_continued_line = FALSE;
6844   boolean reading_playfield = FALSE;
6845   boolean got_valid_playfield_line = FALSE;
6846   boolean invalid_playfield_char = FALSE;
6847   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6848   int file_level_nr = 0;
6849   int x = 0, y = 0;             // initialized to make compilers happy
6850
6851   last_comment[0] = '\0';
6852   level_name[0] = '\0';
6853
6854   if (!(file = openFile(filename, MODE_READ)))
6855   {
6856     level->no_valid_file = TRUE;
6857
6858     if (!level_info_only)
6859       Warn("cannot read level '%s' -- using empty level", filename);
6860
6861     return;
6862   }
6863
6864   while (!checkEndOfFile(file))
6865   {
6866     // level successfully read, but next level may follow here
6867     if (!got_valid_playfield_line && reading_playfield)
6868     {
6869       // read playfield from single level file -- skip remaining file
6870       if (!level_file_info->packed)
6871         break;
6872
6873       if (file_level_nr >= num_levels_to_skip)
6874         break;
6875
6876       file_level_nr++;
6877
6878       last_comment[0] = '\0';
6879       level_name[0] = '\0';
6880
6881       reading_playfield = FALSE;
6882     }
6883
6884     got_valid_playfield_line = FALSE;
6885
6886     // read next line of input file
6887     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6888       break;
6889
6890     // cut trailing line break (this can be newline and/or carriage return)
6891     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6892       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6893         *line_ptr = '\0';
6894
6895     // copy raw input line for later use (mainly debugging output)
6896     strcpy(line_raw, line);
6897
6898     if (read_continued_line)
6899     {
6900       // append new line to existing line, if there is enough space
6901       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6902         strcat(previous_line, line_ptr);
6903
6904       strcpy(line, previous_line);      // copy storage buffer to line
6905
6906       read_continued_line = FALSE;
6907     }
6908
6909     // if the last character is '\', continue at next line
6910     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6911     {
6912       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6913       strcpy(previous_line, line);      // copy line to storage buffer
6914
6915       read_continued_line = TRUE;
6916
6917       continue;
6918     }
6919
6920     // skip empty lines
6921     if (line[0] == '\0')
6922       continue;
6923
6924     // extract comment text from comment line
6925     if (line[0] == ';')
6926     {
6927       for (line_ptr = line; *line_ptr; line_ptr++)
6928         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6929           break;
6930
6931       strcpy(last_comment, line_ptr);
6932
6933       continue;
6934     }
6935
6936     // extract level title text from line containing level title
6937     if (line[0] == '\'')
6938     {
6939       strcpy(level_name, &line[1]);
6940
6941       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6942         level_name[strlen(level_name) - 1] = '\0';
6943
6944       continue;
6945     }
6946
6947     // skip lines containing only spaces (or empty lines)
6948     for (line_ptr = line; *line_ptr; line_ptr++)
6949       if (*line_ptr != ' ')
6950         break;
6951     if (*line_ptr == '\0')
6952       continue;
6953
6954     // at this point, we have found a line containing part of a playfield
6955
6956     got_valid_playfield_line = TRUE;
6957
6958     if (!reading_playfield)
6959     {
6960       reading_playfield = TRUE;
6961       invalid_playfield_char = FALSE;
6962
6963       for (x = 0; x < MAX_LEV_FIELDX; x++)
6964         for (y = 0; y < MAX_LEV_FIELDY; y++)
6965           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6966
6967       level->fieldx = 0;
6968       level->fieldy = 0;
6969
6970       // start with topmost tile row
6971       y = 0;
6972     }
6973
6974     // skip playfield line if larger row than allowed
6975     if (y >= MAX_LEV_FIELDY)
6976       continue;
6977
6978     // start with leftmost tile column
6979     x = 0;
6980
6981     // read playfield elements from line
6982     for (line_ptr = line; *line_ptr; line_ptr++)
6983     {
6984       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
6985
6986       // stop parsing playfield line if larger column than allowed
6987       if (x >= MAX_LEV_FIELDX)
6988         break;
6989
6990       if (mapped_sb_element == EL_UNDEFINED)
6991       {
6992         invalid_playfield_char = TRUE;
6993
6994         break;
6995       }
6996
6997       level->field[x][y] = mapped_sb_element;
6998
6999       // continue with next tile column
7000       x++;
7001
7002       level->fieldx = MAX(x, level->fieldx);
7003     }
7004
7005     if (invalid_playfield_char)
7006     {
7007       // if first playfield line, treat invalid lines as comment lines
7008       if (y == 0)
7009         reading_playfield = FALSE;
7010
7011       continue;
7012     }
7013
7014     // continue with next tile row
7015     y++;
7016   }
7017
7018   closeFile(file);
7019
7020   level->fieldy = y;
7021
7022   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7023   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7024
7025   if (!reading_playfield)
7026   {
7027     level->no_valid_file = TRUE;
7028
7029     Warn("cannot read level '%s' -- using empty level", filename);
7030
7031     return;
7032   }
7033
7034   if (*level_name != '\0')
7035   {
7036     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7037     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7038   }
7039   else if (*last_comment != '\0')
7040   {
7041     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7042     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7043   }
7044   else
7045   {
7046     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7047   }
7048
7049   // set all empty fields beyond the border walls to invisible steel wall
7050   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7051   {
7052     if ((x == 0 || x == level->fieldx - 1 ||
7053          y == 0 || y == level->fieldy - 1) &&
7054         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7055       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7056                      level->field, level->fieldx, level->fieldy);
7057   }
7058
7059   // set special level settings for Sokoban levels
7060   SetLevelSettings_SB(level);
7061
7062   if (load_xsb_to_ces)
7063   {
7064     // special global settings can now be set in level template
7065     level->use_custom_template = TRUE;
7066   }
7067 }
7068
7069
7070 // -------------------------------------------------------------------------
7071 // functions for handling native levels
7072 // -------------------------------------------------------------------------
7073
7074 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7075                                      struct LevelFileInfo *level_file_info,
7076                                      boolean level_info_only)
7077 {
7078   int pos = 0;
7079
7080   // determine position of requested level inside level package
7081   if (level_file_info->packed)
7082     pos = level_file_info->nr - leveldir_current->first_level;
7083
7084   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7085     level->no_valid_file = TRUE;
7086 }
7087
7088 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7089                                      struct LevelFileInfo *level_file_info,
7090                                      boolean level_info_only)
7091 {
7092   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7093     level->no_valid_file = TRUE;
7094 }
7095
7096 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7097                                      struct LevelFileInfo *level_file_info,
7098                                      boolean level_info_only)
7099 {
7100   int pos = 0;
7101
7102   // determine position of requested level inside level package
7103   if (level_file_info->packed)
7104     pos = level_file_info->nr - leveldir_current->first_level;
7105
7106   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7107     level->no_valid_file = TRUE;
7108 }
7109
7110 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7111                                      struct LevelFileInfo *level_file_info,
7112                                      boolean level_info_only)
7113 {
7114   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7115     level->no_valid_file = TRUE;
7116 }
7117
7118 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7119 {
7120   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7121     CopyNativeLevel_RND_to_BD(level);
7122   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7123     CopyNativeLevel_RND_to_EM(level);
7124   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7125     CopyNativeLevel_RND_to_SP(level);
7126   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7127     CopyNativeLevel_RND_to_MM(level);
7128 }
7129
7130 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7131 {
7132   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7133     CopyNativeLevel_BD_to_RND(level);
7134   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7135     CopyNativeLevel_EM_to_RND(level);
7136   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7137     CopyNativeLevel_SP_to_RND(level);
7138   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7139     CopyNativeLevel_MM_to_RND(level);
7140 }
7141
7142 void SaveNativeLevel(struct LevelInfo *level)
7143 {
7144   // saving native level files only supported for some game engines
7145   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7146       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7147     return;
7148
7149   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7150                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7151   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7152   char *filename = getLevelFilenameFromBasename(basename);
7153
7154   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7155     return;
7156
7157   boolean success = FALSE;
7158
7159   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7160   {
7161     CopyNativeLevel_RND_to_BD(level);
7162     // CopyNativeTape_RND_to_BD(level);
7163
7164     success = SaveNativeLevel_BD(filename);
7165   }
7166   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7167   {
7168     CopyNativeLevel_RND_to_SP(level);
7169     CopyNativeTape_RND_to_SP(level);
7170
7171     success = SaveNativeLevel_SP(filename);
7172   }
7173
7174   if (success)
7175     Request("Native level file saved!", REQ_CONFIRM);
7176   else
7177     Request("Failed to save native level file!", REQ_CONFIRM);
7178 }
7179
7180
7181 // ----------------------------------------------------------------------------
7182 // functions for loading generic level
7183 // ----------------------------------------------------------------------------
7184
7185 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7186                                   struct LevelFileInfo *level_file_info,
7187                                   boolean level_info_only)
7188 {
7189   // always start with reliable default values
7190   setLevelInfoToDefaults(level, level_info_only, TRUE);
7191
7192   switch (level_file_info->type)
7193   {
7194     case LEVEL_FILE_TYPE_RND:
7195       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7196       break;
7197
7198     case LEVEL_FILE_TYPE_BD:
7199       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7200       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7201       break;
7202
7203     case LEVEL_FILE_TYPE_EM:
7204       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7205       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7206       break;
7207
7208     case LEVEL_FILE_TYPE_SP:
7209       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7210       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7211       break;
7212
7213     case LEVEL_FILE_TYPE_MM:
7214       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7215       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7216       break;
7217
7218     case LEVEL_FILE_TYPE_DC:
7219       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7220       break;
7221
7222     case LEVEL_FILE_TYPE_SB:
7223       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7224       break;
7225
7226     default:
7227       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7228       break;
7229   }
7230
7231   // if level file is invalid, restore level structure to default values
7232   if (level->no_valid_file)
7233     setLevelInfoToDefaults(level, level_info_only, FALSE);
7234
7235   if (check_special_flags("use_native_bd_game_engine"))
7236     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7237
7238   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7239     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7240
7241   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7242     CopyNativeLevel_Native_to_RND(level);
7243 }
7244
7245 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7246 {
7247   static struct LevelFileInfo level_file_info;
7248
7249   // always start with reliable default values
7250   setFileInfoToDefaults(&level_file_info);
7251
7252   level_file_info.nr = 0;                       // unknown level number
7253   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7254
7255   setString(&level_file_info.filename, filename);
7256
7257   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7258 }
7259
7260 static void LoadLevel_InitVersion(struct LevelInfo *level)
7261 {
7262   int i, j;
7263
7264   if (leveldir_current == NULL)         // only when dumping level
7265     return;
7266
7267   // all engine modifications also valid for levels which use latest engine
7268   if (level->game_version < VERSION_IDENT(3,2,0,5))
7269   {
7270     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7271     level->time_score_base = 10;
7272   }
7273
7274   if (leveldir_current->latest_engine)
7275   {
7276     // ---------- use latest game engine --------------------------------------
7277
7278     /* For all levels which are forced to use the latest game engine version
7279        (normally all but user contributed, private and undefined levels), set
7280        the game engine version to the actual version; this allows for actual
7281        corrections in the game engine to take effect for existing, converted
7282        levels (from "classic" or other existing games) to make the emulation
7283        of the corresponding game more accurate, while (hopefully) not breaking
7284        existing levels created from other players. */
7285
7286     level->game_version = GAME_VERSION_ACTUAL;
7287
7288     /* Set special EM style gems behaviour: EM style gems slip down from
7289        normal, steel and growing wall. As this is a more fundamental change,
7290        it seems better to set the default behaviour to "off" (as it is more
7291        natural) and make it configurable in the level editor (as a property
7292        of gem style elements). Already existing converted levels (neither
7293        private nor contributed levels) are changed to the new behaviour. */
7294
7295     if (level->file_version < FILE_VERSION_2_0)
7296       level->em_slippery_gems = TRUE;
7297
7298     return;
7299   }
7300
7301   // ---------- use game engine the level was created with --------------------
7302
7303   /* For all levels which are not forced to use the latest game engine
7304      version (normally user contributed, private and undefined levels),
7305      use the version of the game engine the levels were created for.
7306
7307      Since 2.0.1, the game engine version is now directly stored
7308      in the level file (chunk "VERS"), so there is no need anymore
7309      to set the game version from the file version (except for old,
7310      pre-2.0 levels, where the game version is still taken from the
7311      file format version used to store the level -- see above). */
7312
7313   // player was faster than enemies in 1.0.0 and before
7314   if (level->file_version == FILE_VERSION_1_0)
7315     for (i = 0; i < MAX_PLAYERS; i++)
7316       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7317
7318   // default behaviour for EM style gems was "slippery" only in 2.0.1
7319   if (level->game_version == VERSION_IDENT(2,0,1,0))
7320     level->em_slippery_gems = TRUE;
7321
7322   // springs could be pushed over pits before (pre-release version) 2.2.0
7323   if (level->game_version < VERSION_IDENT(2,2,0,0))
7324     level->use_spring_bug = TRUE;
7325
7326   if (level->game_version < VERSION_IDENT(3,2,0,5))
7327   {
7328     // time orb caused limited time in endless time levels before 3.2.0-5
7329     level->use_time_orb_bug = TRUE;
7330
7331     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7332     level->block_snap_field = FALSE;
7333
7334     // extra time score was same value as time left score before 3.2.0-5
7335     level->extra_time_score = level->score[SC_TIME_BONUS];
7336   }
7337
7338   if (level->game_version < VERSION_IDENT(3,2,0,7))
7339   {
7340     // default behaviour for snapping was "not continuous" before 3.2.0-7
7341     level->continuous_snapping = FALSE;
7342   }
7343
7344   // only few elements were able to actively move into acid before 3.1.0
7345   // trigger settings did not exist before 3.1.0; set to default "any"
7346   if (level->game_version < VERSION_IDENT(3,1,0,0))
7347   {
7348     // correct "can move into acid" settings (all zero in old levels)
7349
7350     level->can_move_into_acid_bits = 0; // nothing can move into acid
7351     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7352
7353     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7354     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7355     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7356     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7357
7358     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7359       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7360
7361     // correct trigger settings (stored as zero == "none" in old levels)
7362
7363     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7364     {
7365       int element = EL_CUSTOM_START + i;
7366       struct ElementInfo *ei = &element_info[element];
7367
7368       for (j = 0; j < ei->num_change_pages; j++)
7369       {
7370         struct ElementChangeInfo *change = &ei->change_page[j];
7371
7372         change->trigger_player = CH_PLAYER_ANY;
7373         change->trigger_page = CH_PAGE_ANY;
7374       }
7375     }
7376   }
7377
7378   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7379   {
7380     int element = EL_CUSTOM_256;
7381     struct ElementInfo *ei = &element_info[element];
7382     struct ElementChangeInfo *change = &ei->change_page[0];
7383
7384     /* This is needed to fix a problem that was caused by a bugfix in function
7385        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7386        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7387        not replace walkable elements, but instead just placed the player on it,
7388        without placing the Sokoban field under the player). Unfortunately, this
7389        breaks "Snake Bite" style levels when the snake is halfway through a door
7390        that just closes (the snake head is still alive and can be moved in this
7391        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7392        player (without Sokoban element) which then gets killed as designed). */
7393
7394     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7395          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7396         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7397       change->target_element = EL_PLAYER_1;
7398   }
7399
7400   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7401   if (level->game_version < VERSION_IDENT(3,2,5,0))
7402   {
7403     /* This is needed to fix a problem that was caused by a bugfix in function
7404        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7405        corrects the behaviour when a custom element changes to another custom
7406        element with a higher element number that has change actions defined.
7407        Normally, only one change per frame is allowed for custom elements.
7408        Therefore, it is checked if a custom element already changed in the
7409        current frame; if it did, subsequent changes are suppressed.
7410        Unfortunately, this is only checked for element changes, but not for
7411        change actions, which are still executed. As the function above loops
7412        through all custom elements from lower to higher, an element change
7413        resulting in a lower CE number won't be checked again, while a target
7414        element with a higher number will also be checked, and potential change
7415        actions will get executed for this CE, too (which is wrong), while
7416        further changes are ignored (which is correct). As this bugfix breaks
7417        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7418        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7419        behaviour for existing levels and tapes that make use of this bug */
7420
7421     level->use_action_after_change_bug = TRUE;
7422   }
7423
7424   // not centering level after relocating player was default only in 3.2.3
7425   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7426     level->shifted_relocation = TRUE;
7427
7428   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7429   if (level->game_version < VERSION_IDENT(3,2,6,0))
7430     level->em_explodes_by_fire = TRUE;
7431
7432   // levels were solved by the first player entering an exit up to 4.1.0.0
7433   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7434     level->solved_by_one_player = TRUE;
7435
7436   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7437   if (level->game_version < VERSION_IDENT(4,1,1,1))
7438     level->use_life_bugs = TRUE;
7439
7440   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7441   if (level->game_version < VERSION_IDENT(4,1,1,1))
7442     level->sb_objects_needed = FALSE;
7443
7444   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7445   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7446     level->finish_dig_collect = FALSE;
7447
7448   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7449   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7450     level->keep_walkable_ce = TRUE;
7451 }
7452
7453 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7454 {
7455   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7456   int x, y;
7457
7458   // check if this level is (not) a Sokoban level
7459   for (y = 0; y < level->fieldy; y++)
7460     for (x = 0; x < level->fieldx; x++)
7461       if (!IS_SB_ELEMENT(Tile[x][y]))
7462         is_sokoban_level = FALSE;
7463
7464   if (is_sokoban_level)
7465   {
7466     // set special level settings for Sokoban levels
7467     SetLevelSettings_SB(level);
7468   }
7469 }
7470
7471 static void LoadLevel_InitSettings(struct LevelInfo *level)
7472 {
7473   // adjust level settings for (non-native) Sokoban-style levels
7474   LoadLevel_InitSettings_SB(level);
7475
7476   // rename levels with title "nameless level" or if renaming is forced
7477   if (leveldir_current->empty_level_name != NULL &&
7478       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7479        leveldir_current->force_level_name))
7480     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7481              leveldir_current->empty_level_name, level_nr);
7482 }
7483
7484 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7485 {
7486   int i, x, y;
7487
7488   // map elements that have changed in newer versions
7489   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7490                                                     level->game_version);
7491   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7492     for (x = 0; x < 3; x++)
7493       for (y = 0; y < 3; y++)
7494         level->yamyam_content[i].e[x][y] =
7495           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7496                                     level->game_version);
7497
7498 }
7499
7500 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7501 {
7502   int i, j;
7503
7504   // map custom element change events that have changed in newer versions
7505   // (these following values were accidentally changed in version 3.0.1)
7506   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7507   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7508   {
7509     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7510     {
7511       int element = EL_CUSTOM_START + i;
7512
7513       // order of checking and copying events to be mapped is important
7514       // (do not change the start and end value -- they are constant)
7515       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7516       {
7517         if (HAS_CHANGE_EVENT(element, j - 2))
7518         {
7519           SET_CHANGE_EVENT(element, j - 2, FALSE);
7520           SET_CHANGE_EVENT(element, j, TRUE);
7521         }
7522       }
7523
7524       // order of checking and copying events to be mapped is important
7525       // (do not change the start and end value -- they are constant)
7526       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7527       {
7528         if (HAS_CHANGE_EVENT(element, j - 1))
7529         {
7530           SET_CHANGE_EVENT(element, j - 1, FALSE);
7531           SET_CHANGE_EVENT(element, j, TRUE);
7532         }
7533       }
7534     }
7535   }
7536
7537   // initialize "can_change" field for old levels with only one change page
7538   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7539   {
7540     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7541     {
7542       int element = EL_CUSTOM_START + i;
7543
7544       if (CAN_CHANGE(element))
7545         element_info[element].change->can_change = TRUE;
7546     }
7547   }
7548
7549   // correct custom element values (for old levels without these options)
7550   if (level->game_version < VERSION_IDENT(3,1,1,0))
7551   {
7552     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7553     {
7554       int element = EL_CUSTOM_START + i;
7555       struct ElementInfo *ei = &element_info[element];
7556
7557       if (ei->access_direction == MV_NO_DIRECTION)
7558         ei->access_direction = MV_ALL_DIRECTIONS;
7559     }
7560   }
7561
7562   // correct custom element values (fix invalid values for all versions)
7563   if (1)
7564   {
7565     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7566     {
7567       int element = EL_CUSTOM_START + i;
7568       struct ElementInfo *ei = &element_info[element];
7569
7570       for (j = 0; j < ei->num_change_pages; j++)
7571       {
7572         struct ElementChangeInfo *change = &ei->change_page[j];
7573
7574         if (change->trigger_player == CH_PLAYER_NONE)
7575           change->trigger_player = CH_PLAYER_ANY;
7576
7577         if (change->trigger_side == CH_SIDE_NONE)
7578           change->trigger_side = CH_SIDE_ANY;
7579       }
7580     }
7581   }
7582
7583   // initialize "can_explode" field for old levels which did not store this
7584   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7585   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7586   {
7587     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7588     {
7589       int element = EL_CUSTOM_START + i;
7590
7591       if (EXPLODES_1X1_OLD(element))
7592         element_info[element].explosion_type = EXPLODES_1X1;
7593
7594       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7595                                              EXPLODES_SMASHED(element) ||
7596                                              EXPLODES_IMPACT(element)));
7597     }
7598   }
7599
7600   // correct previously hard-coded move delay values for maze runner style
7601   if (level->game_version < VERSION_IDENT(3,1,1,0))
7602   {
7603     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7604     {
7605       int element = EL_CUSTOM_START + i;
7606
7607       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7608       {
7609         // previously hard-coded and therefore ignored
7610         element_info[element].move_delay_fixed = 9;
7611         element_info[element].move_delay_random = 0;
7612       }
7613     }
7614   }
7615
7616   // set some other uninitialized values of custom elements in older levels
7617   if (level->game_version < VERSION_IDENT(3,1,0,0))
7618   {
7619     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7620     {
7621       int element = EL_CUSTOM_START + i;
7622
7623       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7624
7625       element_info[element].explosion_delay = 17;
7626       element_info[element].ignition_delay = 8;
7627     }
7628   }
7629
7630   // set mouse click change events to work for left/middle/right mouse button
7631   if (level->game_version < VERSION_IDENT(4,2,3,0))
7632   {
7633     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7634     {
7635       int element = EL_CUSTOM_START + i;
7636       struct ElementInfo *ei = &element_info[element];
7637
7638       for (j = 0; j < ei->num_change_pages; j++)
7639       {
7640         struct ElementChangeInfo *change = &ei->change_page[j];
7641
7642         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7643             change->has_event[CE_PRESSED_BY_MOUSE] ||
7644             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7645             change->has_event[CE_MOUSE_PRESSED_ON_X])
7646           change->trigger_side = CH_SIDE_ANY;
7647       }
7648     }
7649   }
7650 }
7651
7652 static void LoadLevel_InitElements(struct LevelInfo *level)
7653 {
7654   LoadLevel_InitStandardElements(level);
7655
7656   if (level->file_has_custom_elements)
7657     LoadLevel_InitCustomElements(level);
7658
7659   // initialize element properties for level editor etc.
7660   InitElementPropertiesEngine(level->game_version);
7661   InitElementPropertiesGfxElement();
7662 }
7663
7664 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7665 {
7666   int x, y;
7667
7668   // map elements that have changed in newer versions
7669   for (y = 0; y < level->fieldy; y++)
7670     for (x = 0; x < level->fieldx; x++)
7671       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7672                                                      level->game_version);
7673
7674   // clear unused playfield data (nicer if level gets resized in editor)
7675   for (x = 0; x < MAX_LEV_FIELDX; x++)
7676     for (y = 0; y < MAX_LEV_FIELDY; y++)
7677       if (x >= level->fieldx || y >= level->fieldy)
7678         level->field[x][y] = EL_EMPTY;
7679
7680   // copy elements to runtime playfield array
7681   for (x = 0; x < MAX_LEV_FIELDX; x++)
7682     for (y = 0; y < MAX_LEV_FIELDY; y++)
7683       Tile[x][y] = level->field[x][y];
7684
7685   // initialize level size variables for faster access
7686   lev_fieldx = level->fieldx;
7687   lev_fieldy = level->fieldy;
7688
7689   // determine border element for this level
7690   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7691     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7692   else
7693     SetBorderElement();
7694 }
7695
7696 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7697 {
7698   struct LevelFileInfo *level_file_info = &level->file_info;
7699
7700   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7701     CopyNativeLevel_RND_to_Native(level);
7702 }
7703
7704 static void LoadLevelTemplate_LoadAndInit(void)
7705 {
7706   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7707
7708   LoadLevel_InitVersion(&level_template);
7709   LoadLevel_InitElements(&level_template);
7710   LoadLevel_InitSettings(&level_template);
7711
7712   ActivateLevelTemplate();
7713 }
7714
7715 void LoadLevelTemplate(int nr)
7716 {
7717   if (!fileExists(getGlobalLevelTemplateFilename()))
7718   {
7719     Warn("no level template found for this level");
7720
7721     return;
7722   }
7723
7724   setLevelFileInfo(&level_template.file_info, nr);
7725
7726   LoadLevelTemplate_LoadAndInit();
7727 }
7728
7729 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7730 {
7731   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7732
7733   LoadLevelTemplate_LoadAndInit();
7734 }
7735
7736 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7737 {
7738   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7739
7740   if (level.use_custom_template)
7741   {
7742     if (network_level != NULL)
7743       LoadNetworkLevelTemplate(network_level);
7744     else
7745       LoadLevelTemplate(-1);
7746   }
7747
7748   LoadLevel_InitVersion(&level);
7749   LoadLevel_InitElements(&level);
7750   LoadLevel_InitPlayfield(&level);
7751   LoadLevel_InitSettings(&level);
7752
7753   LoadLevel_InitNativeEngines(&level);
7754 }
7755
7756 void LoadLevel(int nr)
7757 {
7758   SetLevelSetInfo(leveldir_current->identifier, nr);
7759
7760   setLevelFileInfo(&level.file_info, nr);
7761
7762   LoadLevel_LoadAndInit(NULL);
7763 }
7764
7765 void LoadLevelInfoOnly(int nr)
7766 {
7767   setLevelFileInfo(&level.file_info, nr);
7768
7769   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7770 }
7771
7772 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7773 {
7774   SetLevelSetInfo(network_level->leveldir_identifier,
7775                   network_level->file_info.nr);
7776
7777   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7778
7779   LoadLevel_LoadAndInit(network_level);
7780 }
7781
7782 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7783 {
7784   int chunk_size = 0;
7785
7786   chunk_size += putFileVersion(file, level->file_version);
7787   chunk_size += putFileVersion(file, level->game_version);
7788
7789   return chunk_size;
7790 }
7791
7792 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7793 {
7794   int chunk_size = 0;
7795
7796   chunk_size += putFile16BitBE(file, level->creation_date.year);
7797   chunk_size += putFile8Bit(file,    level->creation_date.month);
7798   chunk_size += putFile8Bit(file,    level->creation_date.day);
7799
7800   return chunk_size;
7801 }
7802
7803 #if ENABLE_HISTORIC_CHUNKS
7804 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7805 {
7806   int i, x, y;
7807
7808   putFile8Bit(file, level->fieldx);
7809   putFile8Bit(file, level->fieldy);
7810
7811   putFile16BitBE(file, level->time);
7812   putFile16BitBE(file, level->gems_needed);
7813
7814   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7815     putFile8Bit(file, level->name[i]);
7816
7817   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7818     putFile8Bit(file, level->score[i]);
7819
7820   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7821     for (y = 0; y < 3; y++)
7822       for (x = 0; x < 3; x++)
7823         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7824                            level->yamyam_content[i].e[x][y]));
7825   putFile8Bit(file, level->amoeba_speed);
7826   putFile8Bit(file, level->time_magic_wall);
7827   putFile8Bit(file, level->time_wheel);
7828   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7829                      level->amoeba_content));
7830   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7831   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7832   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7833   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7834
7835   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7836
7837   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7838   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7839   putFile32BitBE(file, level->can_move_into_acid_bits);
7840   putFile8Bit(file, level->dont_collide_with_bits);
7841
7842   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7843   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7844
7845   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7846   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7847   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7848
7849   putFile8Bit(file, level->game_engine_type);
7850
7851   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7852 }
7853 #endif
7854
7855 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7856 {
7857   int chunk_size = 0;
7858   int i;
7859
7860   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7861     chunk_size += putFile8Bit(file, level->name[i]);
7862
7863   return chunk_size;
7864 }
7865
7866 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7867 {
7868   int chunk_size = 0;
7869   int i;
7870
7871   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7872     chunk_size += putFile8Bit(file, level->author[i]);
7873
7874   return chunk_size;
7875 }
7876
7877 #if ENABLE_HISTORIC_CHUNKS
7878 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7879 {
7880   int chunk_size = 0;
7881   int x, y;
7882
7883   for (y = 0; y < level->fieldy; y++)
7884     for (x = 0; x < level->fieldx; x++)
7885       if (level->encoding_16bit_field)
7886         chunk_size += putFile16BitBE(file, level->field[x][y]);
7887       else
7888         chunk_size += putFile8Bit(file, level->field[x][y]);
7889
7890   return chunk_size;
7891 }
7892 #endif
7893
7894 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7895 {
7896   int chunk_size = 0;
7897   int x, y;
7898
7899   for (y = 0; y < level->fieldy; y++) 
7900     for (x = 0; x < level->fieldx; x++) 
7901       chunk_size += putFile16BitBE(file, level->field[x][y]);
7902
7903   return chunk_size;
7904 }
7905
7906 #if ENABLE_HISTORIC_CHUNKS
7907 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7908 {
7909   int i, x, y;
7910
7911   putFile8Bit(file, EL_YAMYAM);
7912   putFile8Bit(file, level->num_yamyam_contents);
7913   putFile8Bit(file, 0);
7914   putFile8Bit(file, 0);
7915
7916   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7917     for (y = 0; y < 3; y++)
7918       for (x = 0; x < 3; x++)
7919         if (level->encoding_16bit_field)
7920           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7921         else
7922           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7923 }
7924 #endif
7925
7926 #if ENABLE_HISTORIC_CHUNKS
7927 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7928 {
7929   int i, x, y;
7930   int num_contents, content_xsize, content_ysize;
7931   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7932
7933   if (element == EL_YAMYAM)
7934   {
7935     num_contents = level->num_yamyam_contents;
7936     content_xsize = 3;
7937     content_ysize = 3;
7938
7939     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7940       for (y = 0; y < 3; y++)
7941         for (x = 0; x < 3; x++)
7942           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7943   }
7944   else if (element == EL_BD_AMOEBA)
7945   {
7946     num_contents = 1;
7947     content_xsize = 1;
7948     content_ysize = 1;
7949
7950     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7951       for (y = 0; y < 3; y++)
7952         for (x = 0; x < 3; x++)
7953           content_array[i][x][y] = EL_EMPTY;
7954     content_array[0][0][0] = level->amoeba_content;
7955   }
7956   else
7957   {
7958     // chunk header already written -- write empty chunk data
7959     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7960
7961     Warn("cannot save content for element '%d'", element);
7962
7963     return;
7964   }
7965
7966   putFile16BitBE(file, element);
7967   putFile8Bit(file, num_contents);
7968   putFile8Bit(file, content_xsize);
7969   putFile8Bit(file, content_ysize);
7970
7971   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7972
7973   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7974     for (y = 0; y < 3; y++)
7975       for (x = 0; x < 3; x++)
7976         putFile16BitBE(file, content_array[i][x][y]);
7977 }
7978 #endif
7979
7980 #if ENABLE_HISTORIC_CHUNKS
7981 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
7982 {
7983   int envelope_nr = element - EL_ENVELOPE_1;
7984   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
7985   int chunk_size = 0;
7986   int i;
7987
7988   chunk_size += putFile16BitBE(file, element);
7989   chunk_size += putFile16BitBE(file, envelope_len);
7990   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
7991   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
7992
7993   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
7994   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
7995
7996   for (i = 0; i < envelope_len; i++)
7997     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
7998
7999   return chunk_size;
8000 }
8001 #endif
8002
8003 #if ENABLE_HISTORIC_CHUNKS
8004 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8005                            int num_changed_custom_elements)
8006 {
8007   int i, check = 0;
8008
8009   putFile16BitBE(file, num_changed_custom_elements);
8010
8011   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8012   {
8013     int element = EL_CUSTOM_START + i;
8014
8015     struct ElementInfo *ei = &element_info[element];
8016
8017     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8018     {
8019       if (check < num_changed_custom_elements)
8020       {
8021         putFile16BitBE(file, element);
8022         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8023       }
8024
8025       check++;
8026     }
8027   }
8028
8029   if (check != num_changed_custom_elements)     // should not happen
8030     Warn("inconsistent number of custom element properties");
8031 }
8032 #endif
8033
8034 #if ENABLE_HISTORIC_CHUNKS
8035 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8036                            int num_changed_custom_elements)
8037 {
8038   int i, check = 0;
8039
8040   putFile16BitBE(file, num_changed_custom_elements);
8041
8042   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8043   {
8044     int element = EL_CUSTOM_START + i;
8045
8046     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8047     {
8048       if (check < num_changed_custom_elements)
8049       {
8050         putFile16BitBE(file, element);
8051         putFile16BitBE(file, element_info[element].change->target_element);
8052       }
8053
8054       check++;
8055     }
8056   }
8057
8058   if (check != num_changed_custom_elements)     // should not happen
8059     Warn("inconsistent number of custom target elements");
8060 }
8061 #endif
8062
8063 #if ENABLE_HISTORIC_CHUNKS
8064 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8065                            int num_changed_custom_elements)
8066 {
8067   int i, j, x, y, check = 0;
8068
8069   putFile16BitBE(file, num_changed_custom_elements);
8070
8071   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8072   {
8073     int element = EL_CUSTOM_START + i;
8074     struct ElementInfo *ei = &element_info[element];
8075
8076     if (ei->modified_settings)
8077     {
8078       if (check < num_changed_custom_elements)
8079       {
8080         putFile16BitBE(file, element);
8081
8082         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8083           putFile8Bit(file, ei->description[j]);
8084
8085         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8086
8087         // some free bytes for future properties and padding
8088         WriteUnusedBytesToFile(file, 7);
8089
8090         putFile8Bit(file, ei->use_gfx_element);
8091         putFile16BitBE(file, ei->gfx_element_initial);
8092
8093         putFile8Bit(file, ei->collect_score_initial);
8094         putFile8Bit(file, ei->collect_count_initial);
8095
8096         putFile16BitBE(file, ei->push_delay_fixed);
8097         putFile16BitBE(file, ei->push_delay_random);
8098         putFile16BitBE(file, ei->move_delay_fixed);
8099         putFile16BitBE(file, ei->move_delay_random);
8100
8101         putFile16BitBE(file, ei->move_pattern);
8102         putFile8Bit(file, ei->move_direction_initial);
8103         putFile8Bit(file, ei->move_stepsize);
8104
8105         for (y = 0; y < 3; y++)
8106           for (x = 0; x < 3; x++)
8107             putFile16BitBE(file, ei->content.e[x][y]);
8108
8109         putFile32BitBE(file, ei->change->events);
8110
8111         putFile16BitBE(file, ei->change->target_element);
8112
8113         putFile16BitBE(file, ei->change->delay_fixed);
8114         putFile16BitBE(file, ei->change->delay_random);
8115         putFile16BitBE(file, ei->change->delay_frames);
8116
8117         putFile16BitBE(file, ei->change->initial_trigger_element);
8118
8119         putFile8Bit(file, ei->change->explode);
8120         putFile8Bit(file, ei->change->use_target_content);
8121         putFile8Bit(file, ei->change->only_if_complete);
8122         putFile8Bit(file, ei->change->use_random_replace);
8123
8124         putFile8Bit(file, ei->change->random_percentage);
8125         putFile8Bit(file, ei->change->replace_when);
8126
8127         for (y = 0; y < 3; y++)
8128           for (x = 0; x < 3; x++)
8129             putFile16BitBE(file, ei->change->content.e[x][y]);
8130
8131         putFile8Bit(file, ei->slippery_type);
8132
8133         // some free bytes for future properties and padding
8134         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8135       }
8136
8137       check++;
8138     }
8139   }
8140
8141   if (check != num_changed_custom_elements)     // should not happen
8142     Warn("inconsistent number of custom element properties");
8143 }
8144 #endif
8145
8146 #if ENABLE_HISTORIC_CHUNKS
8147 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8148 {
8149   struct ElementInfo *ei = &element_info[element];
8150   int i, j, x, y;
8151
8152   // ---------- custom element base property values (96 bytes) ----------------
8153
8154   putFile16BitBE(file, element);
8155
8156   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8157     putFile8Bit(file, ei->description[i]);
8158
8159   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8160
8161   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8162
8163   putFile8Bit(file, ei->num_change_pages);
8164
8165   putFile16BitBE(file, ei->ce_value_fixed_initial);
8166   putFile16BitBE(file, ei->ce_value_random_initial);
8167   putFile8Bit(file, ei->use_last_ce_value);
8168
8169   putFile8Bit(file, ei->use_gfx_element);
8170   putFile16BitBE(file, ei->gfx_element_initial);
8171
8172   putFile8Bit(file, ei->collect_score_initial);
8173   putFile8Bit(file, ei->collect_count_initial);
8174
8175   putFile8Bit(file, ei->drop_delay_fixed);
8176   putFile8Bit(file, ei->push_delay_fixed);
8177   putFile8Bit(file, ei->drop_delay_random);
8178   putFile8Bit(file, ei->push_delay_random);
8179   putFile16BitBE(file, ei->move_delay_fixed);
8180   putFile16BitBE(file, ei->move_delay_random);
8181
8182   // bits 0 - 15 of "move_pattern" ...
8183   putFile16BitBE(file, ei->move_pattern & 0xffff);
8184   putFile8Bit(file, ei->move_direction_initial);
8185   putFile8Bit(file, ei->move_stepsize);
8186
8187   putFile8Bit(file, ei->slippery_type);
8188
8189   for (y = 0; y < 3; y++)
8190     for (x = 0; x < 3; x++)
8191       putFile16BitBE(file, ei->content.e[x][y]);
8192
8193   putFile16BitBE(file, ei->move_enter_element);
8194   putFile16BitBE(file, ei->move_leave_element);
8195   putFile8Bit(file, ei->move_leave_type);
8196
8197   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8198   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8199
8200   putFile8Bit(file, ei->access_direction);
8201
8202   putFile8Bit(file, ei->explosion_delay);
8203   putFile8Bit(file, ei->ignition_delay);
8204   putFile8Bit(file, ei->explosion_type);
8205
8206   // some free bytes for future custom property values and padding
8207   WriteUnusedBytesToFile(file, 1);
8208
8209   // ---------- change page property values (48 bytes) ------------------------
8210
8211   for (i = 0; i < ei->num_change_pages; i++)
8212   {
8213     struct ElementChangeInfo *change = &ei->change_page[i];
8214     unsigned int event_bits;
8215
8216     // bits 0 - 31 of "has_event[]" ...
8217     event_bits = 0;
8218     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8219       if (change->has_event[j])
8220         event_bits |= (1u << j);
8221     putFile32BitBE(file, event_bits);
8222
8223     putFile16BitBE(file, change->target_element);
8224
8225     putFile16BitBE(file, change->delay_fixed);
8226     putFile16BitBE(file, change->delay_random);
8227     putFile16BitBE(file, change->delay_frames);
8228
8229     putFile16BitBE(file, change->initial_trigger_element);
8230
8231     putFile8Bit(file, change->explode);
8232     putFile8Bit(file, change->use_target_content);
8233     putFile8Bit(file, change->only_if_complete);
8234     putFile8Bit(file, change->use_random_replace);
8235
8236     putFile8Bit(file, change->random_percentage);
8237     putFile8Bit(file, change->replace_when);
8238
8239     for (y = 0; y < 3; y++)
8240       for (x = 0; x < 3; x++)
8241         putFile16BitBE(file, change->target_content.e[x][y]);
8242
8243     putFile8Bit(file, change->can_change);
8244
8245     putFile8Bit(file, change->trigger_side);
8246
8247     putFile8Bit(file, change->trigger_player);
8248     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8249                        log_2(change->trigger_page)));
8250
8251     putFile8Bit(file, change->has_action);
8252     putFile8Bit(file, change->action_type);
8253     putFile8Bit(file, change->action_mode);
8254     putFile16BitBE(file, change->action_arg);
8255
8256     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8257     event_bits = 0;
8258     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8259       if (change->has_event[j])
8260         event_bits |= (1u << (j - 32));
8261     putFile8Bit(file, event_bits);
8262   }
8263 }
8264 #endif
8265
8266 #if ENABLE_HISTORIC_CHUNKS
8267 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8268 {
8269   struct ElementInfo *ei = &element_info[element];
8270   struct ElementGroupInfo *group = ei->group;
8271   int i;
8272
8273   putFile16BitBE(file, element);
8274
8275   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8276     putFile8Bit(file, ei->description[i]);
8277
8278   putFile8Bit(file, group->num_elements);
8279
8280   putFile8Bit(file, ei->use_gfx_element);
8281   putFile16BitBE(file, ei->gfx_element_initial);
8282
8283   putFile8Bit(file, group->choice_mode);
8284
8285   // some free bytes for future values and padding
8286   WriteUnusedBytesToFile(file, 3);
8287
8288   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8289     putFile16BitBE(file, group->element[i]);
8290 }
8291 #endif
8292
8293 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8294                                 boolean write_element)
8295 {
8296   int save_type = entry->save_type;
8297   int data_type = entry->data_type;
8298   int conf_type = entry->conf_type;
8299   int byte_mask = conf_type & CONF_MASK_BYTES;
8300   int element = entry->element;
8301   int default_value = entry->default_value;
8302   int num_bytes = 0;
8303   boolean modified = FALSE;
8304
8305   if (byte_mask != CONF_MASK_MULTI_BYTES)
8306   {
8307     void *value_ptr = entry->value;
8308     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8309                  *(int *)value_ptr);
8310
8311     // check if any settings have been modified before saving them
8312     if (value != default_value)
8313       modified = TRUE;
8314
8315     // do not save if explicitly told or if unmodified default settings
8316     if ((save_type == SAVE_CONF_NEVER) ||
8317         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8318       return 0;
8319
8320     if (write_element)
8321       num_bytes += putFile16BitBE(file, element);
8322
8323     num_bytes += putFile8Bit(file, conf_type);
8324     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8325                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8326                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8327                   0);
8328   }
8329   else if (data_type == TYPE_STRING)
8330   {
8331     char *default_string = entry->default_string;
8332     char *string = (char *)(entry->value);
8333     int string_length = strlen(string);
8334     int i;
8335
8336     // check if any settings have been modified before saving them
8337     if (!strEqual(string, default_string))
8338       modified = TRUE;
8339
8340     // do not save if explicitly told or if unmodified default settings
8341     if ((save_type == SAVE_CONF_NEVER) ||
8342         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8343       return 0;
8344
8345     if (write_element)
8346       num_bytes += putFile16BitBE(file, element);
8347
8348     num_bytes += putFile8Bit(file, conf_type);
8349     num_bytes += putFile16BitBE(file, string_length);
8350
8351     for (i = 0; i < string_length; i++)
8352       num_bytes += putFile8Bit(file, string[i]);
8353   }
8354   else if (data_type == TYPE_ELEMENT_LIST)
8355   {
8356     int *element_array = (int *)(entry->value);
8357     int num_elements = *(int *)(entry->num_entities);
8358     int i;
8359
8360     // check if any settings have been modified before saving them
8361     for (i = 0; i < num_elements; i++)
8362       if (element_array[i] != default_value)
8363         modified = TRUE;
8364
8365     // do not save if explicitly told or if unmodified default settings
8366     if ((save_type == SAVE_CONF_NEVER) ||
8367         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8368       return 0;
8369
8370     if (write_element)
8371       num_bytes += putFile16BitBE(file, element);
8372
8373     num_bytes += putFile8Bit(file, conf_type);
8374     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8375
8376     for (i = 0; i < num_elements; i++)
8377       num_bytes += putFile16BitBE(file, element_array[i]);
8378   }
8379   else if (data_type == TYPE_CONTENT_LIST)
8380   {
8381     struct Content *content = (struct Content *)(entry->value);
8382     int num_contents = *(int *)(entry->num_entities);
8383     int i, x, y;
8384
8385     // check if any settings have been modified before saving them
8386     for (i = 0; i < num_contents; i++)
8387       for (y = 0; y < 3; y++)
8388         for (x = 0; x < 3; x++)
8389           if (content[i].e[x][y] != default_value)
8390             modified = TRUE;
8391
8392     // do not save if explicitly told or if unmodified default settings
8393     if ((save_type == SAVE_CONF_NEVER) ||
8394         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8395       return 0;
8396
8397     if (write_element)
8398       num_bytes += putFile16BitBE(file, element);
8399
8400     num_bytes += putFile8Bit(file, conf_type);
8401     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8402
8403     for (i = 0; i < num_contents; i++)
8404       for (y = 0; y < 3; y++)
8405         for (x = 0; x < 3; x++)
8406           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8407   }
8408
8409   return num_bytes;
8410 }
8411
8412 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8413 {
8414   int chunk_size = 0;
8415   int i;
8416
8417   li = *level;          // copy level data into temporary buffer
8418
8419   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8420     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8421
8422   return chunk_size;
8423 }
8424
8425 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8426 {
8427   int chunk_size = 0;
8428   int i;
8429
8430   li = *level;          // copy level data into temporary buffer
8431
8432   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8433     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8434
8435   return chunk_size;
8436 }
8437
8438 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8439 {
8440   int envelope_nr = element - EL_ENVELOPE_1;
8441   int chunk_size = 0;
8442   int i;
8443
8444   chunk_size += putFile16BitBE(file, element);
8445
8446   // copy envelope data into temporary buffer
8447   xx_envelope = level->envelope[envelope_nr];
8448
8449   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8450     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8451
8452   return chunk_size;
8453 }
8454
8455 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8456 {
8457   struct ElementInfo *ei = &element_info[element];
8458   int chunk_size = 0;
8459   int i, j;
8460
8461   chunk_size += putFile16BitBE(file, element);
8462
8463   xx_ei = *ei;          // copy element 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_CUSX_base[i].data_type != -1; i++)
8469     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8470
8471   for (i = 0; i < ei->num_change_pages; i++)
8472   {
8473     struct ElementChangeInfo *change = &ei->change_page[i];
8474
8475     xx_current_change_page = i;
8476
8477     xx_change = *change;        // copy change data into temporary buffer
8478
8479     resetEventBits();
8480     setEventBitsFromEventFlags(change);
8481
8482     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8483       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8484                                          FALSE);
8485   }
8486
8487   return chunk_size;
8488 }
8489
8490 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8491 {
8492   struct ElementInfo *ei = &element_info[element];
8493   struct ElementGroupInfo *group = ei->group;
8494   int chunk_size = 0;
8495   int i;
8496
8497   chunk_size += putFile16BitBE(file, element);
8498
8499   xx_ei = *ei;          // copy element data into temporary buffer
8500   xx_group = *group;    // copy group data into temporary buffer
8501
8502   // set default description string for this specific element
8503   strcpy(xx_default_description, getDefaultElementDescription(ei));
8504
8505   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8506     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8507
8508   return chunk_size;
8509 }
8510
8511 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8512 {
8513   struct ElementInfo *ei = &element_info[element];
8514   int chunk_size = 0;
8515   int i;
8516
8517   chunk_size += putFile16BitBE(file, element);
8518
8519   xx_ei = *ei;          // copy element data into temporary buffer
8520
8521   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8522     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8523
8524   return chunk_size;
8525 }
8526
8527 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8528                                   boolean save_as_template)
8529 {
8530   int chunk_size;
8531   int i;
8532   FILE *file;
8533
8534   if (!(file = fopen(filename, MODE_WRITE)))
8535   {
8536     Warn("cannot save level file '%s'", filename);
8537
8538     return;
8539   }
8540
8541   level->file_version = FILE_VERSION_ACTUAL;
8542   level->game_version = GAME_VERSION_ACTUAL;
8543
8544   level->creation_date = getCurrentDate();
8545
8546   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8547   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8548
8549   chunk_size = SaveLevel_VERS(NULL, level);
8550   putFileChunkBE(file, "VERS", chunk_size);
8551   SaveLevel_VERS(file, level);
8552
8553   chunk_size = SaveLevel_DATE(NULL, level);
8554   putFileChunkBE(file, "DATE", chunk_size);
8555   SaveLevel_DATE(file, level);
8556
8557   chunk_size = SaveLevel_NAME(NULL, level);
8558   putFileChunkBE(file, "NAME", chunk_size);
8559   SaveLevel_NAME(file, level);
8560
8561   chunk_size = SaveLevel_AUTH(NULL, level);
8562   putFileChunkBE(file, "AUTH", chunk_size);
8563   SaveLevel_AUTH(file, level);
8564
8565   chunk_size = SaveLevel_INFO(NULL, level);
8566   putFileChunkBE(file, "INFO", chunk_size);
8567   SaveLevel_INFO(file, level);
8568
8569   chunk_size = SaveLevel_BODY(NULL, level);
8570   putFileChunkBE(file, "BODY", chunk_size);
8571   SaveLevel_BODY(file, level);
8572
8573   chunk_size = SaveLevel_ELEM(NULL, level);
8574   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8575   {
8576     putFileChunkBE(file, "ELEM", chunk_size);
8577     SaveLevel_ELEM(file, level);
8578   }
8579
8580   for (i = 0; i < NUM_ENVELOPES; i++)
8581   {
8582     int element = EL_ENVELOPE_1 + i;
8583
8584     chunk_size = SaveLevel_NOTE(NULL, level, element);
8585     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8586     {
8587       putFileChunkBE(file, "NOTE", chunk_size);
8588       SaveLevel_NOTE(file, level, element);
8589     }
8590   }
8591
8592   // if not using template level, check for non-default custom/group elements
8593   if (!level->use_custom_template || save_as_template)
8594   {
8595     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8596     {
8597       int element = EL_CUSTOM_START + i;
8598
8599       chunk_size = SaveLevel_CUSX(NULL, level, element);
8600       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8601       {
8602         putFileChunkBE(file, "CUSX", chunk_size);
8603         SaveLevel_CUSX(file, level, element);
8604       }
8605     }
8606
8607     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8608     {
8609       int element = EL_GROUP_START + i;
8610
8611       chunk_size = SaveLevel_GRPX(NULL, level, element);
8612       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8613       {
8614         putFileChunkBE(file, "GRPX", chunk_size);
8615         SaveLevel_GRPX(file, level, element);
8616       }
8617     }
8618
8619     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8620     {
8621       int element = GET_EMPTY_ELEMENT(i);
8622
8623       chunk_size = SaveLevel_EMPX(NULL, level, element);
8624       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8625       {
8626         putFileChunkBE(file, "EMPX", chunk_size);
8627         SaveLevel_EMPX(file, level, element);
8628       }
8629     }
8630   }
8631
8632   fclose(file);
8633
8634   SetFilePermissions(filename, PERMS_PRIVATE);
8635 }
8636
8637 void SaveLevel(int nr)
8638 {
8639   char *filename = getDefaultLevelFilename(nr);
8640
8641   SaveLevelFromFilename(&level, filename, FALSE);
8642 }
8643
8644 void SaveLevelTemplate(void)
8645 {
8646   char *filename = getLocalLevelTemplateFilename();
8647
8648   SaveLevelFromFilename(&level, filename, TRUE);
8649 }
8650
8651 boolean SaveLevelChecked(int nr)
8652 {
8653   char *filename = getDefaultLevelFilename(nr);
8654   boolean new_level = !fileExists(filename);
8655   boolean level_saved = FALSE;
8656
8657   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8658   {
8659     SaveLevel(nr);
8660
8661     if (new_level)
8662       Request("Level saved!", REQ_CONFIRM);
8663
8664     level_saved = TRUE;
8665   }
8666
8667   return level_saved;
8668 }
8669
8670 void DumpLevel(struct LevelInfo *level)
8671 {
8672   if (level->no_level_file || level->no_valid_file)
8673   {
8674     Warn("cannot dump -- no valid level file found");
8675
8676     return;
8677   }
8678
8679   PrintLine("-", 79);
8680   Print("Level xxx (file version %08d, game version %08d)\n",
8681         level->file_version, level->game_version);
8682   PrintLine("-", 79);
8683
8684   Print("Level author: '%s'\n", level->author);
8685   Print("Level title:  '%s'\n", level->name);
8686   Print("\n");
8687   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8688   Print("\n");
8689   Print("Level time:  %d seconds\n", level->time);
8690   Print("Gems needed: %d\n", level->gems_needed);
8691   Print("\n");
8692   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8693   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8694   Print("Time for light:      %d seconds\n", level->time_light);
8695   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8696   Print("\n");
8697   Print("Amoeba speed: %d\n", level->amoeba_speed);
8698   Print("\n");
8699
8700   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8701   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8702   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8703   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8704   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8705   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8706
8707   if (options.debug)
8708   {
8709     int i, j;
8710
8711     for (i = 0; i < NUM_ENVELOPES; i++)
8712     {
8713       char *text = level->envelope[i].text;
8714       int text_len = strlen(text);
8715       boolean has_text = FALSE;
8716
8717       for (j = 0; j < text_len; j++)
8718         if (text[j] != ' ' && text[j] != '\n')
8719           has_text = TRUE;
8720
8721       if (has_text)
8722       {
8723         Print("\n");
8724         Print("Envelope %d:\n'%s'\n", i + 1, text);
8725       }
8726     }
8727   }
8728
8729   PrintLine("-", 79);
8730 }
8731
8732 void DumpLevels(void)
8733 {
8734   static LevelDirTree *dumplevel_leveldir = NULL;
8735
8736   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8737                                                  global.dumplevel_leveldir);
8738
8739   if (dumplevel_leveldir == NULL)
8740     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8741
8742   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8743       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8744     Fail("no such level number: %d", global.dumplevel_level_nr);
8745
8746   leveldir_current = dumplevel_leveldir;
8747
8748   LoadLevel(global.dumplevel_level_nr);
8749   DumpLevel(&level);
8750
8751   CloseAllAndExit(0);
8752 }
8753
8754
8755 // ============================================================================
8756 // tape file functions
8757 // ============================================================================
8758
8759 static void setTapeInfoToDefaults(void)
8760 {
8761   int i;
8762
8763   // always start with reliable default values (empty tape)
8764   TapeErase();
8765
8766   // default values (also for pre-1.2 tapes) with only the first player
8767   tape.player_participates[0] = TRUE;
8768   for (i = 1; i < MAX_PLAYERS; i++)
8769     tape.player_participates[i] = FALSE;
8770
8771   // at least one (default: the first) player participates in every tape
8772   tape.num_participating_players = 1;
8773
8774   tape.property_bits = TAPE_PROPERTY_NONE;
8775
8776   tape.level_nr = level_nr;
8777   tape.counter = 0;
8778   tape.changed = FALSE;
8779   tape.solved = FALSE;
8780
8781   tape.recording = FALSE;
8782   tape.playing = FALSE;
8783   tape.pausing = FALSE;
8784
8785   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8786   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8787
8788   tape.no_info_chunk = TRUE;
8789   tape.no_valid_file = FALSE;
8790 }
8791
8792 static int getTapePosSize(struct TapeInfo *tape)
8793 {
8794   int tape_pos_size = 0;
8795
8796   if (tape->use_key_actions)
8797     tape_pos_size += tape->num_participating_players;
8798
8799   if (tape->use_mouse_actions)
8800     tape_pos_size += 3;         // x and y position and mouse button mask
8801
8802   tape_pos_size += 1;           // tape action delay value
8803
8804   return tape_pos_size;
8805 }
8806
8807 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8808 {
8809   tape->use_key_actions = FALSE;
8810   tape->use_mouse_actions = FALSE;
8811
8812   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8813     tape->use_key_actions = TRUE;
8814
8815   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8816     tape->use_mouse_actions = TRUE;
8817 }
8818
8819 static int getTapeActionValue(struct TapeInfo *tape)
8820 {
8821   return (tape->use_key_actions &&
8822           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8823           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8824           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8825           TAPE_ACTIONS_DEFAULT);
8826 }
8827
8828 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8829 {
8830   tape->file_version = getFileVersion(file);
8831   tape->game_version = getFileVersion(file);
8832
8833   return chunk_size;
8834 }
8835
8836 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8837 {
8838   int i;
8839
8840   tape->random_seed = getFile32BitBE(file);
8841   tape->date        = getFile32BitBE(file);
8842   tape->length      = getFile32BitBE(file);
8843
8844   // read header fields that are new since version 1.2
8845   if (tape->file_version >= FILE_VERSION_1_2)
8846   {
8847     byte store_participating_players = getFile8Bit(file);
8848     int engine_version;
8849
8850     // since version 1.2, tapes store which players participate in the tape
8851     tape->num_participating_players = 0;
8852     for (i = 0; i < MAX_PLAYERS; i++)
8853     {
8854       tape->player_participates[i] = FALSE;
8855
8856       if (store_participating_players & (1 << i))
8857       {
8858         tape->player_participates[i] = TRUE;
8859         tape->num_participating_players++;
8860       }
8861     }
8862
8863     setTapeActionFlags(tape, getFile8Bit(file));
8864
8865     tape->property_bits = getFile8Bit(file);
8866     tape->solved = getFile8Bit(file);
8867
8868     engine_version = getFileVersion(file);
8869     if (engine_version > 0)
8870       tape->engine_version = engine_version;
8871     else
8872       tape->engine_version = tape->game_version;
8873   }
8874
8875   return chunk_size;
8876 }
8877
8878 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8879 {
8880   tape->scr_fieldx = getFile8Bit(file);
8881   tape->scr_fieldy = getFile8Bit(file);
8882
8883   return chunk_size;
8884 }
8885
8886 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8887 {
8888   char *level_identifier = NULL;
8889   int level_identifier_size;
8890   int i;
8891
8892   tape->no_info_chunk = FALSE;
8893
8894   level_identifier_size = getFile16BitBE(file);
8895
8896   level_identifier = checked_malloc(level_identifier_size);
8897
8898   for (i = 0; i < level_identifier_size; i++)
8899     level_identifier[i] = getFile8Bit(file);
8900
8901   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8902   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8903
8904   checked_free(level_identifier);
8905
8906   tape->level_nr = getFile16BitBE(file);
8907
8908   chunk_size = 2 + level_identifier_size + 2;
8909
8910   return chunk_size;
8911 }
8912
8913 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8914 {
8915   int i, j;
8916   int tape_pos_size = getTapePosSize(tape);
8917   int chunk_size_expected = tape_pos_size * tape->length;
8918
8919   if (chunk_size_expected != chunk_size)
8920   {
8921     ReadUnusedBytesFromFile(file, chunk_size);
8922     return chunk_size_expected;
8923   }
8924
8925   for (i = 0; i < tape->length; i++)
8926   {
8927     if (i >= MAX_TAPE_LEN)
8928     {
8929       Warn("tape truncated -- size exceeds maximum tape size %d",
8930             MAX_TAPE_LEN);
8931
8932       // tape too large; read and ignore remaining tape data from this chunk
8933       for (;i < tape->length; i++)
8934         ReadUnusedBytesFromFile(file, tape_pos_size);
8935
8936       break;
8937     }
8938
8939     if (tape->use_key_actions)
8940     {
8941       for (j = 0; j < MAX_PLAYERS; j++)
8942       {
8943         tape->pos[i].action[j] = MV_NONE;
8944
8945         if (tape->player_participates[j])
8946           tape->pos[i].action[j] = getFile8Bit(file);
8947       }
8948     }
8949
8950     if (tape->use_mouse_actions)
8951     {
8952       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8953       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8954       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8955     }
8956
8957     tape->pos[i].delay = getFile8Bit(file);
8958
8959     if (tape->file_version == FILE_VERSION_1_0)
8960     {
8961       // eliminate possible diagonal moves in old tapes
8962       // this is only for backward compatibility
8963
8964       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8965       byte action = tape->pos[i].action[0];
8966       int k, num_moves = 0;
8967
8968       for (k = 0; k < 4; k++)
8969       {
8970         if (action & joy_dir[k])
8971         {
8972           tape->pos[i + num_moves].action[0] = joy_dir[k];
8973           if (num_moves > 0)
8974             tape->pos[i + num_moves].delay = 0;
8975           num_moves++;
8976         }
8977       }
8978
8979       if (num_moves > 1)
8980       {
8981         num_moves--;
8982         i += num_moves;
8983         tape->length += num_moves;
8984       }
8985     }
8986     else if (tape->file_version < FILE_VERSION_2_0)
8987     {
8988       // convert pre-2.0 tapes to new tape format
8989
8990       if (tape->pos[i].delay > 1)
8991       {
8992         // action part
8993         tape->pos[i + 1] = tape->pos[i];
8994         tape->pos[i + 1].delay = 1;
8995
8996         // delay part
8997         for (j = 0; j < MAX_PLAYERS; j++)
8998           tape->pos[i].action[j] = MV_NONE;
8999         tape->pos[i].delay--;
9000
9001         i++;
9002         tape->length++;
9003       }
9004     }
9005
9006     if (checkEndOfFile(file))
9007       break;
9008   }
9009
9010   if (i != tape->length)
9011     chunk_size = tape_pos_size * i;
9012
9013   return chunk_size;
9014 }
9015
9016 static void LoadTape_SokobanSolution(char *filename)
9017 {
9018   File *file;
9019   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9020
9021   if (!(file = openFile(filename, MODE_READ)))
9022   {
9023     tape.no_valid_file = TRUE;
9024
9025     return;
9026   }
9027
9028   while (!checkEndOfFile(file))
9029   {
9030     unsigned char c = getByteFromFile(file);
9031
9032     if (checkEndOfFile(file))
9033       break;
9034
9035     switch (c)
9036     {
9037       case 'u':
9038       case 'U':
9039         tape.pos[tape.length].action[0] = MV_UP;
9040         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9041         tape.length++;
9042         break;
9043
9044       case 'd':
9045       case 'D':
9046         tape.pos[tape.length].action[0] = MV_DOWN;
9047         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9048         tape.length++;
9049         break;
9050
9051       case 'l':
9052       case 'L':
9053         tape.pos[tape.length].action[0] = MV_LEFT;
9054         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9055         tape.length++;
9056         break;
9057
9058       case 'r':
9059       case 'R':
9060         tape.pos[tape.length].action[0] = MV_RIGHT;
9061         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9062         tape.length++;
9063         break;
9064
9065       case '\n':
9066       case '\r':
9067       case '\t':
9068       case ' ':
9069         // ignore white-space characters
9070         break;
9071
9072       default:
9073         tape.no_valid_file = TRUE;
9074
9075         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9076
9077         break;
9078     }
9079   }
9080
9081   closeFile(file);
9082
9083   if (tape.no_valid_file)
9084     return;
9085
9086   tape.length_frames  = GetTapeLengthFrames();
9087   tape.length_seconds = GetTapeLengthSeconds();
9088 }
9089
9090 void LoadTapeFromFilename(char *filename)
9091 {
9092   char cookie[MAX_LINE_LEN];
9093   char chunk_name[CHUNK_ID_LEN + 1];
9094   File *file;
9095   int chunk_size;
9096
9097   // always start with reliable default values
9098   setTapeInfoToDefaults();
9099
9100   if (strSuffix(filename, ".sln"))
9101   {
9102     LoadTape_SokobanSolution(filename);
9103
9104     return;
9105   }
9106
9107   if (!(file = openFile(filename, MODE_READ)))
9108   {
9109     tape.no_valid_file = TRUE;
9110
9111     return;
9112   }
9113
9114   getFileChunkBE(file, chunk_name, NULL);
9115   if (strEqual(chunk_name, "RND1"))
9116   {
9117     getFile32BitBE(file);               // not used
9118
9119     getFileChunkBE(file, chunk_name, NULL);
9120     if (!strEqual(chunk_name, "TAPE"))
9121     {
9122       tape.no_valid_file = TRUE;
9123
9124       Warn("unknown format of tape file '%s'", filename);
9125
9126       closeFile(file);
9127
9128       return;
9129     }
9130   }
9131   else  // check for pre-2.0 file format with cookie string
9132   {
9133     strcpy(cookie, chunk_name);
9134     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9135       cookie[4] = '\0';
9136     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9137       cookie[strlen(cookie) - 1] = '\0';
9138
9139     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9140     {
9141       tape.no_valid_file = TRUE;
9142
9143       Warn("unknown format of tape file '%s'", filename);
9144
9145       closeFile(file);
9146
9147       return;
9148     }
9149
9150     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9151     {
9152       tape.no_valid_file = TRUE;
9153
9154       Warn("unsupported version of tape file '%s'", filename);
9155
9156       closeFile(file);
9157
9158       return;
9159     }
9160
9161     // pre-2.0 tape files have no game version, so use file version here
9162     tape.game_version = tape.file_version;
9163   }
9164
9165   if (tape.file_version < FILE_VERSION_1_2)
9166   {
9167     // tape files from versions before 1.2.0 without chunk structure
9168     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9169     LoadTape_BODY(file, 2 * tape.length,      &tape);
9170   }
9171   else
9172   {
9173     static struct
9174     {
9175       char *name;
9176       int size;
9177       int (*loader)(File *, int, struct TapeInfo *);
9178     }
9179     chunk_info[] =
9180     {
9181       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9182       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9183       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9184       { "INFO", -1,                     LoadTape_INFO },
9185       { "BODY", -1,                     LoadTape_BODY },
9186       {  NULL,  0,                      NULL }
9187     };
9188
9189     while (getFileChunkBE(file, chunk_name, &chunk_size))
9190     {
9191       int i = 0;
9192
9193       while (chunk_info[i].name != NULL &&
9194              !strEqual(chunk_name, chunk_info[i].name))
9195         i++;
9196
9197       if (chunk_info[i].name == NULL)
9198       {
9199         Warn("unknown chunk '%s' in tape file '%s'",
9200               chunk_name, filename);
9201
9202         ReadUnusedBytesFromFile(file, chunk_size);
9203       }
9204       else if (chunk_info[i].size != -1 &&
9205                chunk_info[i].size != chunk_size)
9206       {
9207         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9208               chunk_size, chunk_name, filename);
9209
9210         ReadUnusedBytesFromFile(file, chunk_size);
9211       }
9212       else
9213       {
9214         // call function to load this tape chunk
9215         int chunk_size_expected =
9216           (chunk_info[i].loader)(file, chunk_size, &tape);
9217
9218         // the size of some chunks cannot be checked before reading other
9219         // chunks first (like "HEAD" and "BODY") that contain some header
9220         // information, so check them here
9221         if (chunk_size_expected != chunk_size)
9222         {
9223           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9224                 chunk_size, chunk_name, filename);
9225         }
9226       }
9227     }
9228   }
9229
9230   closeFile(file);
9231
9232   tape.length_frames  = GetTapeLengthFrames();
9233   tape.length_seconds = GetTapeLengthSeconds();
9234
9235 #if 0
9236   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9237         tape.file_version);
9238   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9239         tape.game_version);
9240   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9241         tape.engine_version);
9242 #endif
9243 }
9244
9245 void LoadTape(int nr)
9246 {
9247   char *filename = getTapeFilename(nr);
9248
9249   LoadTapeFromFilename(filename);
9250 }
9251
9252 void LoadSolutionTape(int nr)
9253 {
9254   char *filename = getSolutionTapeFilename(nr);
9255
9256   LoadTapeFromFilename(filename);
9257
9258   if (TAPE_IS_EMPTY(tape))
9259   {
9260     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9261         level.native_bd_level->replay != NULL)
9262       CopyNativeTape_BD_to_RND(&level);
9263     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9264         level.native_sp_level->demo.is_available)
9265       CopyNativeTape_SP_to_RND(&level);
9266   }
9267 }
9268
9269 void LoadScoreTape(char *score_tape_basename, int nr)
9270 {
9271   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9272
9273   LoadTapeFromFilename(filename);
9274 }
9275
9276 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9277 {
9278   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9279
9280   LoadTapeFromFilename(filename);
9281 }
9282
9283 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9284 {
9285   // chunk required for team mode tapes with non-default screen size
9286   return (tape->num_participating_players > 1 &&
9287           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9288            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9289 }
9290
9291 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9292 {
9293   putFileVersion(file, tape->file_version);
9294   putFileVersion(file, tape->game_version);
9295 }
9296
9297 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9298 {
9299   int i;
9300   byte store_participating_players = 0;
9301
9302   // set bits for participating players for compact storage
9303   for (i = 0; i < MAX_PLAYERS; i++)
9304     if (tape->player_participates[i])
9305       store_participating_players |= (1 << i);
9306
9307   putFile32BitBE(file, tape->random_seed);
9308   putFile32BitBE(file, tape->date);
9309   putFile32BitBE(file, tape->length);
9310
9311   putFile8Bit(file, store_participating_players);
9312
9313   putFile8Bit(file, getTapeActionValue(tape));
9314
9315   putFile8Bit(file, tape->property_bits);
9316   putFile8Bit(file, tape->solved);
9317
9318   putFileVersion(file, tape->engine_version);
9319 }
9320
9321 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9322 {
9323   putFile8Bit(file, tape->scr_fieldx);
9324   putFile8Bit(file, tape->scr_fieldy);
9325 }
9326
9327 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9328 {
9329   int level_identifier_size = strlen(tape->level_identifier) + 1;
9330   int i;
9331
9332   putFile16BitBE(file, level_identifier_size);
9333
9334   for (i = 0; i < level_identifier_size; i++)
9335     putFile8Bit(file, tape->level_identifier[i]);
9336
9337   putFile16BitBE(file, tape->level_nr);
9338 }
9339
9340 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9341 {
9342   int i, j;
9343
9344   for (i = 0; i < tape->length; i++)
9345   {
9346     if (tape->use_key_actions)
9347     {
9348       for (j = 0; j < MAX_PLAYERS; j++)
9349         if (tape->player_participates[j])
9350           putFile8Bit(file, tape->pos[i].action[j]);
9351     }
9352
9353     if (tape->use_mouse_actions)
9354     {
9355       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9356       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9357       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9358     }
9359
9360     putFile8Bit(file, tape->pos[i].delay);
9361   }
9362 }
9363
9364 void SaveTapeToFilename(char *filename)
9365 {
9366   FILE *file;
9367   int tape_pos_size;
9368   int info_chunk_size;
9369   int body_chunk_size;
9370
9371   if (!(file = fopen(filename, MODE_WRITE)))
9372   {
9373     Warn("cannot save level recording file '%s'", filename);
9374
9375     return;
9376   }
9377
9378   tape_pos_size = getTapePosSize(&tape);
9379
9380   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9381   body_chunk_size = tape_pos_size * tape.length;
9382
9383   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9384   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9385
9386   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9387   SaveTape_VERS(file, &tape);
9388
9389   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9390   SaveTape_HEAD(file, &tape);
9391
9392   if (checkSaveTape_SCRN(&tape))
9393   {
9394     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9395     SaveTape_SCRN(file, &tape);
9396   }
9397
9398   putFileChunkBE(file, "INFO", info_chunk_size);
9399   SaveTape_INFO(file, &tape);
9400
9401   putFileChunkBE(file, "BODY", body_chunk_size);
9402   SaveTape_BODY(file, &tape);
9403
9404   fclose(file);
9405
9406   SetFilePermissions(filename, PERMS_PRIVATE);
9407 }
9408
9409 static void SaveTapeExt(char *filename)
9410 {
9411   int i;
9412
9413   tape.file_version = FILE_VERSION_ACTUAL;
9414   tape.game_version = GAME_VERSION_ACTUAL;
9415
9416   tape.num_participating_players = 0;
9417
9418   // count number of participating players
9419   for (i = 0; i < MAX_PLAYERS; i++)
9420     if (tape.player_participates[i])
9421       tape.num_participating_players++;
9422
9423   SaveTapeToFilename(filename);
9424
9425   tape.changed = FALSE;
9426 }
9427
9428 void SaveTape(int nr)
9429 {
9430   char *filename = getTapeFilename(nr);
9431
9432   InitTapeDirectory(leveldir_current->subdir);
9433
9434   SaveTapeExt(filename);
9435 }
9436
9437 void SaveScoreTape(int nr)
9438 {
9439   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9440
9441   // used instead of "leveldir_current->subdir" (for network games)
9442   InitScoreTapeDirectory(levelset.identifier, nr);
9443
9444   SaveTapeExt(filename);
9445 }
9446
9447 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9448                                   unsigned int req_state_added)
9449 {
9450   char *filename = getTapeFilename(nr);
9451   boolean new_tape = !fileExists(filename);
9452   boolean tape_saved = FALSE;
9453
9454   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9455   {
9456     SaveTape(nr);
9457
9458     if (new_tape)
9459       Request(msg_saved, REQ_CONFIRM | req_state_added);
9460
9461     tape_saved = TRUE;
9462   }
9463
9464   return tape_saved;
9465 }
9466
9467 boolean SaveTapeChecked(int nr)
9468 {
9469   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9470 }
9471
9472 boolean SaveTapeChecked_LevelSolved(int nr)
9473 {
9474   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9475                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9476 }
9477
9478 void DumpTape(struct TapeInfo *tape)
9479 {
9480   int tape_frame_counter;
9481   int i, j;
9482
9483   if (tape->no_valid_file)
9484   {
9485     Warn("cannot dump -- no valid tape file found");
9486
9487     return;
9488   }
9489
9490   PrintLine("-", 79);
9491
9492   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9493         tape->level_nr, tape->file_version, tape->game_version);
9494   Print("                  (effective engine version %08d)\n",
9495         tape->engine_version);
9496   Print("Level series identifier: '%s'\n", tape->level_identifier);
9497
9498   Print("Solution tape: %s\n",
9499         tape->solved ? "yes" :
9500         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9501
9502   Print("Special tape properties: ");
9503   if (tape->property_bits == TAPE_PROPERTY_NONE)
9504     Print("[none]");
9505   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9506     Print("[em_random_bug]");
9507   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9508     Print("[game_speed]");
9509   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9510     Print("[pause]");
9511   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9512     Print("[single_step]");
9513   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9514     Print("[snapshot]");
9515   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9516     Print("[replayed]");
9517   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9518     Print("[tas_keys]");
9519   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9520     Print("[small_graphics]");
9521   Print("\n");
9522
9523   int year2 = tape->date / 10000;
9524   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9525   int month_index_raw = (tape->date / 100) % 100;
9526   int month_index = month_index_raw % 12;       // prevent invalid index
9527   int month = month_index + 1;
9528   int day = tape->date % 100;
9529
9530   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9531
9532   PrintLine("-", 79);
9533
9534   tape_frame_counter = 0;
9535
9536   for (i = 0; i < tape->length; i++)
9537   {
9538     if (i >= MAX_TAPE_LEN)
9539       break;
9540
9541     Print("%04d: ", i);
9542
9543     for (j = 0; j < MAX_PLAYERS; j++)
9544     {
9545       if (tape->player_participates[j])
9546       {
9547         int action = tape->pos[i].action[j];
9548
9549         Print("%d:%02x ", j, action);
9550         Print("[%c%c%c%c|%c%c] - ",
9551               (action & JOY_LEFT ? '<' : ' '),
9552               (action & JOY_RIGHT ? '>' : ' '),
9553               (action & JOY_UP ? '^' : ' '),
9554               (action & JOY_DOWN ? 'v' : ' '),
9555               (action & JOY_BUTTON_1 ? '1' : ' '),
9556               (action & JOY_BUTTON_2 ? '2' : ' '));
9557       }
9558     }
9559
9560     Print("(%03d) ", tape->pos[i].delay);
9561     Print("[%05d]\n", tape_frame_counter);
9562
9563     tape_frame_counter += tape->pos[i].delay;
9564   }
9565
9566   PrintLine("-", 79);
9567 }
9568
9569 void DumpTapes(void)
9570 {
9571   static LevelDirTree *dumptape_leveldir = NULL;
9572
9573   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9574                                                 global.dumptape_leveldir);
9575
9576   if (dumptape_leveldir == NULL)
9577     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9578
9579   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9580       global.dumptape_level_nr > dumptape_leveldir->last_level)
9581     Fail("no such level number: %d", global.dumptape_level_nr);
9582
9583   leveldir_current = dumptape_leveldir;
9584
9585   if (options.mytapes)
9586     LoadTape(global.dumptape_level_nr);
9587   else
9588     LoadSolutionTape(global.dumptape_level_nr);
9589
9590   DumpTape(&tape);
9591
9592   CloseAllAndExit(0);
9593 }
9594
9595
9596 // ============================================================================
9597 // score file functions
9598 // ============================================================================
9599
9600 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9601 {
9602   int i;
9603
9604   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9605   {
9606     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9607     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9608     scores->entry[i].score = 0;
9609     scores->entry[i].time = 0;
9610
9611     scores->entry[i].id = -1;
9612     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9613     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9614     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9615     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9616     strcpy(scores->entry[i].country_code, "??");
9617   }
9618
9619   scores->num_entries = 0;
9620   scores->last_added = -1;
9621   scores->last_added_local = -1;
9622
9623   scores->updated = FALSE;
9624   scores->uploaded = FALSE;
9625   scores->tape_downloaded = FALSE;
9626   scores->force_last_added = FALSE;
9627
9628   // The following values are intentionally not reset here:
9629   // - last_level_nr
9630   // - last_entry_nr
9631   // - next_level_nr
9632   // - continue_playing
9633   // - continue_on_return
9634 }
9635
9636 static void setScoreInfoToDefaults(void)
9637 {
9638   setScoreInfoToDefaultsExt(&scores);
9639 }
9640
9641 static void setServerScoreInfoToDefaults(void)
9642 {
9643   setScoreInfoToDefaultsExt(&server_scores);
9644 }
9645
9646 static void LoadScore_OLD(int nr)
9647 {
9648   int i;
9649   char *filename = getScoreFilename(nr);
9650   char cookie[MAX_LINE_LEN];
9651   char line[MAX_LINE_LEN];
9652   char *line_ptr;
9653   FILE *file;
9654
9655   if (!(file = fopen(filename, MODE_READ)))
9656     return;
9657
9658   // check file identifier
9659   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9660     cookie[0] = '\0';
9661   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9662     cookie[strlen(cookie) - 1] = '\0';
9663
9664   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9665   {
9666     Warn("unknown format of score file '%s'", filename);
9667
9668     fclose(file);
9669
9670     return;
9671   }
9672
9673   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9674   {
9675     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9676       Warn("fscanf() failed; %s", strerror(errno));
9677
9678     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9679       line[0] = '\0';
9680
9681     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9682       line[strlen(line) - 1] = '\0';
9683
9684     for (line_ptr = line; *line_ptr; line_ptr++)
9685     {
9686       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9687       {
9688         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9689         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9690         break;
9691       }
9692     }
9693   }
9694
9695   fclose(file);
9696 }
9697
9698 static void ConvertScore_OLD(void)
9699 {
9700   // only convert score to time for levels that rate playing time over score
9701   if (!level.rate_time_over_score)
9702     return;
9703
9704   // convert old score to playing time for score-less levels (like Supaplex)
9705   int time_final_max = 999;
9706   int i;
9707
9708   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9709   {
9710     int score = scores.entry[i].score;
9711
9712     if (score > 0 && score < time_final_max)
9713       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9714   }
9715 }
9716
9717 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9718 {
9719   scores->file_version = getFileVersion(file);
9720   scores->game_version = getFileVersion(file);
9721
9722   return chunk_size;
9723 }
9724
9725 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9726 {
9727   char *level_identifier = NULL;
9728   int level_identifier_size;
9729   int i;
9730
9731   level_identifier_size = getFile16BitBE(file);
9732
9733   level_identifier = checked_malloc(level_identifier_size);
9734
9735   for (i = 0; i < level_identifier_size; i++)
9736     level_identifier[i] = getFile8Bit(file);
9737
9738   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9739   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9740
9741   checked_free(level_identifier);
9742
9743   scores->level_nr = getFile16BitBE(file);
9744   scores->num_entries = getFile16BitBE(file);
9745
9746   chunk_size = 2 + level_identifier_size + 2 + 2;
9747
9748   return chunk_size;
9749 }
9750
9751 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9752 {
9753   int i, j;
9754
9755   for (i = 0; i < scores->num_entries; i++)
9756   {
9757     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9758       scores->entry[i].name[j] = getFile8Bit(file);
9759
9760     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9761   }
9762
9763   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9764
9765   return chunk_size;
9766 }
9767
9768 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9769 {
9770   int i;
9771
9772   for (i = 0; i < scores->num_entries; i++)
9773     scores->entry[i].score = getFile16BitBE(file);
9774
9775   chunk_size = scores->num_entries * 2;
9776
9777   return chunk_size;
9778 }
9779
9780 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9781 {
9782   int i;
9783
9784   for (i = 0; i < scores->num_entries; i++)
9785     scores->entry[i].score = getFile32BitBE(file);
9786
9787   chunk_size = scores->num_entries * 4;
9788
9789   return chunk_size;
9790 }
9791
9792 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9793 {
9794   int i;
9795
9796   for (i = 0; i < scores->num_entries; i++)
9797     scores->entry[i].time = getFile32BitBE(file);
9798
9799   chunk_size = scores->num_entries * 4;
9800
9801   return chunk_size;
9802 }
9803
9804 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9805 {
9806   int i, j;
9807
9808   for (i = 0; i < scores->num_entries; i++)
9809   {
9810     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9811       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9812
9813     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9814   }
9815
9816   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9817
9818   return chunk_size;
9819 }
9820
9821 void LoadScore(int nr)
9822 {
9823   char *filename = getScoreFilename(nr);
9824   char cookie[MAX_LINE_LEN];
9825   char chunk_name[CHUNK_ID_LEN + 1];
9826   int chunk_size;
9827   boolean old_score_file_format = FALSE;
9828   File *file;
9829
9830   // always start with reliable default values
9831   setScoreInfoToDefaults();
9832
9833   if (!(file = openFile(filename, MODE_READ)))
9834     return;
9835
9836   getFileChunkBE(file, chunk_name, NULL);
9837   if (strEqual(chunk_name, "RND1"))
9838   {
9839     getFile32BitBE(file);               // not used
9840
9841     getFileChunkBE(file, chunk_name, NULL);
9842     if (!strEqual(chunk_name, "SCOR"))
9843     {
9844       Warn("unknown format of score file '%s'", filename);
9845
9846       closeFile(file);
9847
9848       return;
9849     }
9850   }
9851   else  // check for old file format with cookie string
9852   {
9853     strcpy(cookie, chunk_name);
9854     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9855       cookie[4] = '\0';
9856     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9857       cookie[strlen(cookie) - 1] = '\0';
9858
9859     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9860     {
9861       Warn("unknown format of score file '%s'", filename);
9862
9863       closeFile(file);
9864
9865       return;
9866     }
9867
9868     old_score_file_format = TRUE;
9869   }
9870
9871   if (old_score_file_format)
9872   {
9873     // score files from versions before 4.2.4.0 without chunk structure
9874     LoadScore_OLD(nr);
9875
9876     // convert score to time, if possible (mainly for Supaplex levels)
9877     ConvertScore_OLD();
9878   }
9879   else
9880   {
9881     static struct
9882     {
9883       char *name;
9884       int size;
9885       int (*loader)(File *, int, struct ScoreInfo *);
9886     }
9887     chunk_info[] =
9888     {
9889       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9890       { "INFO", -1,                     LoadScore_INFO },
9891       { "NAME", -1,                     LoadScore_NAME },
9892       { "SCOR", -1,                     LoadScore_SCOR },
9893       { "SC4R", -1,                     LoadScore_SC4R },
9894       { "TIME", -1,                     LoadScore_TIME },
9895       { "TAPE", -1,                     LoadScore_TAPE },
9896
9897       {  NULL,  0,                      NULL }
9898     };
9899
9900     while (getFileChunkBE(file, chunk_name, &chunk_size))
9901     {
9902       int i = 0;
9903
9904       while (chunk_info[i].name != NULL &&
9905              !strEqual(chunk_name, chunk_info[i].name))
9906         i++;
9907
9908       if (chunk_info[i].name == NULL)
9909       {
9910         Warn("unknown chunk '%s' in score file '%s'",
9911               chunk_name, filename);
9912
9913         ReadUnusedBytesFromFile(file, chunk_size);
9914       }
9915       else if (chunk_info[i].size != -1 &&
9916                chunk_info[i].size != chunk_size)
9917       {
9918         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9919               chunk_size, chunk_name, filename);
9920
9921         ReadUnusedBytesFromFile(file, chunk_size);
9922       }
9923       else
9924       {
9925         // call function to load this score chunk
9926         int chunk_size_expected =
9927           (chunk_info[i].loader)(file, chunk_size, &scores);
9928
9929         // the size of some chunks cannot be checked before reading other
9930         // chunks first (like "HEAD" and "BODY") that contain some header
9931         // information, so check them here
9932         if (chunk_size_expected != chunk_size)
9933         {
9934           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9935                 chunk_size, chunk_name, filename);
9936         }
9937       }
9938     }
9939   }
9940
9941   closeFile(file);
9942 }
9943
9944 #if ENABLE_HISTORIC_CHUNKS
9945 void SaveScore_OLD(int nr)
9946 {
9947   int i;
9948   char *filename = getScoreFilename(nr);
9949   FILE *file;
9950
9951   // used instead of "leveldir_current->subdir" (for network games)
9952   InitScoreDirectory(levelset.identifier);
9953
9954   if (!(file = fopen(filename, MODE_WRITE)))
9955   {
9956     Warn("cannot save score for level %d", nr);
9957
9958     return;
9959   }
9960
9961   fprintf(file, "%s\n\n", SCORE_COOKIE);
9962
9963   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9964     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9965
9966   fclose(file);
9967
9968   SetFilePermissions(filename, PERMS_PRIVATE);
9969 }
9970 #endif
9971
9972 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9973 {
9974   putFileVersion(file, scores->file_version);
9975   putFileVersion(file, scores->game_version);
9976 }
9977
9978 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
9979 {
9980   int level_identifier_size = strlen(scores->level_identifier) + 1;
9981   int i;
9982
9983   putFile16BitBE(file, level_identifier_size);
9984
9985   for (i = 0; i < level_identifier_size; i++)
9986     putFile8Bit(file, scores->level_identifier[i]);
9987
9988   putFile16BitBE(file, scores->level_nr);
9989   putFile16BitBE(file, scores->num_entries);
9990 }
9991
9992 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
9993 {
9994   int i, j;
9995
9996   for (i = 0; i < scores->num_entries; i++)
9997   {
9998     int name_size = strlen(scores->entry[i].name);
9999
10000     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10001       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10002   }
10003 }
10004
10005 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10006 {
10007   int i;
10008
10009   for (i = 0; i < scores->num_entries; i++)
10010     putFile16BitBE(file, scores->entry[i].score);
10011 }
10012
10013 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10014 {
10015   int i;
10016
10017   for (i = 0; i < scores->num_entries; i++)
10018     putFile32BitBE(file, scores->entry[i].score);
10019 }
10020
10021 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10022 {
10023   int i;
10024
10025   for (i = 0; i < scores->num_entries; i++)
10026     putFile32BitBE(file, scores->entry[i].time);
10027 }
10028
10029 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10030 {
10031   int i, j;
10032
10033   for (i = 0; i < scores->num_entries; i++)
10034   {
10035     int size = strlen(scores->entry[i].tape_basename);
10036
10037     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10038       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10039   }
10040 }
10041
10042 static void SaveScoreToFilename(char *filename)
10043 {
10044   FILE *file;
10045   int info_chunk_size;
10046   int name_chunk_size;
10047   int scor_chunk_size;
10048   int sc4r_chunk_size;
10049   int time_chunk_size;
10050   int tape_chunk_size;
10051   boolean has_large_score_values;
10052   int i;
10053
10054   if (!(file = fopen(filename, MODE_WRITE)))
10055   {
10056     Warn("cannot save score file '%s'", filename);
10057
10058     return;
10059   }
10060
10061   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10062   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10063   scor_chunk_size = scores.num_entries * 2;
10064   sc4r_chunk_size = scores.num_entries * 4;
10065   time_chunk_size = scores.num_entries * 4;
10066   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10067
10068   has_large_score_values = FALSE;
10069   for (i = 0; i < scores.num_entries; i++)
10070     if (scores.entry[i].score > 0xffff)
10071       has_large_score_values = TRUE;
10072
10073   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10074   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10075
10076   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10077   SaveScore_VERS(file, &scores);
10078
10079   putFileChunkBE(file, "INFO", info_chunk_size);
10080   SaveScore_INFO(file, &scores);
10081
10082   putFileChunkBE(file, "NAME", name_chunk_size);
10083   SaveScore_NAME(file, &scores);
10084
10085   if (has_large_score_values)
10086   {
10087     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10088     SaveScore_SC4R(file, &scores);
10089   }
10090   else
10091   {
10092     putFileChunkBE(file, "SCOR", scor_chunk_size);
10093     SaveScore_SCOR(file, &scores);
10094   }
10095
10096   putFileChunkBE(file, "TIME", time_chunk_size);
10097   SaveScore_TIME(file, &scores);
10098
10099   putFileChunkBE(file, "TAPE", tape_chunk_size);
10100   SaveScore_TAPE(file, &scores);
10101
10102   fclose(file);
10103
10104   SetFilePermissions(filename, PERMS_PRIVATE);
10105 }
10106
10107 void SaveScore(int nr)
10108 {
10109   char *filename = getScoreFilename(nr);
10110   int i;
10111
10112   // used instead of "leveldir_current->subdir" (for network games)
10113   InitScoreDirectory(levelset.identifier);
10114
10115   scores.file_version = FILE_VERSION_ACTUAL;
10116   scores.game_version = GAME_VERSION_ACTUAL;
10117
10118   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10119   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10120   scores.level_nr = level_nr;
10121
10122   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10123     if (scores.entry[i].score == 0 &&
10124         scores.entry[i].time == 0 &&
10125         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10126       break;
10127
10128   scores.num_entries = i;
10129
10130   if (scores.num_entries == 0)
10131     return;
10132
10133   SaveScoreToFilename(filename);
10134 }
10135
10136 static void LoadServerScoreFromCache(int nr)
10137 {
10138   struct ScoreEntry score_entry;
10139   struct
10140   {
10141     void *value;
10142     boolean is_string;
10143     int string_size;
10144   }
10145   score_mapping[] =
10146   {
10147     { &score_entry.score,               FALSE,  0                       },
10148     { &score_entry.time,                FALSE,  0                       },
10149     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10150     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10151     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10152     { &score_entry.id,                  FALSE,  0                       },
10153     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10154     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10155     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10156     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10157
10158     { NULL,                             FALSE,  0                       }
10159   };
10160   char *filename = getScoreCacheFilename(nr);
10161   SetupFileHash *score_hash = loadSetupFileHash(filename);
10162   int i, j;
10163
10164   server_scores.num_entries = 0;
10165
10166   if (score_hash == NULL)
10167     return;
10168
10169   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10170   {
10171     score_entry = server_scores.entry[i];
10172
10173     for (j = 0; score_mapping[j].value != NULL; j++)
10174     {
10175       char token[10];
10176
10177       sprintf(token, "%02d.%d", i, j);
10178
10179       char *value = getHashEntry(score_hash, token);
10180
10181       if (value == NULL)
10182         continue;
10183
10184       if (score_mapping[j].is_string)
10185       {
10186         char *score_value = (char *)score_mapping[j].value;
10187         int value_size = score_mapping[j].string_size;
10188
10189         strncpy(score_value, value, value_size);
10190         score_value[value_size] = '\0';
10191       }
10192       else
10193       {
10194         int *score_value = (int *)score_mapping[j].value;
10195
10196         *score_value = atoi(value);
10197       }
10198
10199       server_scores.num_entries = i + 1;
10200     }
10201
10202     server_scores.entry[i] = score_entry;
10203   }
10204
10205   freeSetupFileHash(score_hash);
10206 }
10207
10208 void LoadServerScore(int nr, boolean download_score)
10209 {
10210   if (!setup.use_api_server)
10211     return;
10212
10213   // always start with reliable default values
10214   setServerScoreInfoToDefaults();
10215
10216   // 1st step: load server scores from cache file (which may not exist)
10217   // (this should prevent reading it while the thread is writing to it)
10218   LoadServerScoreFromCache(nr);
10219
10220   if (download_score && runtime.use_api_server)
10221   {
10222     // 2nd step: download server scores from score server to cache file
10223     // (as thread, as it might time out if the server is not reachable)
10224     ApiGetScoreAsThread(nr);
10225   }
10226 }
10227
10228 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10229 {
10230   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10231
10232   // if score tape not uploaded, ask for uploading missing tapes later
10233   if (!setup.has_remaining_tapes)
10234     setup.ask_for_remaining_tapes = TRUE;
10235
10236   setup.provide_uploading_tapes = TRUE;
10237   setup.has_remaining_tapes = TRUE;
10238
10239   SaveSetup_ServerSetup();
10240 }
10241
10242 void SaveServerScore(int nr, boolean tape_saved)
10243 {
10244   if (!runtime.use_api_server)
10245   {
10246     PrepareScoreTapesForUpload(leveldir_current->subdir);
10247
10248     return;
10249   }
10250
10251   ApiAddScoreAsThread(nr, tape_saved, NULL);
10252 }
10253
10254 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10255                              char *score_tape_filename)
10256 {
10257   if (!runtime.use_api_server)
10258     return;
10259
10260   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10261 }
10262
10263 void LoadLocalAndServerScore(int nr, boolean download_score)
10264 {
10265   int last_added_local = scores.last_added_local;
10266   boolean force_last_added = scores.force_last_added;
10267
10268   // needed if only showing server scores
10269   setScoreInfoToDefaults();
10270
10271   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10272     LoadScore(nr);
10273
10274   // restore last added local score entry (before merging server scores)
10275   scores.last_added = scores.last_added_local = last_added_local;
10276
10277   if (setup.use_api_server &&
10278       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10279   {
10280     // load server scores from cache file and trigger update from server
10281     LoadServerScore(nr, download_score);
10282
10283     // merge local scores with scores from server
10284     MergeServerScore();
10285   }
10286
10287   if (force_last_added)
10288     scores.force_last_added = force_last_added;
10289 }
10290
10291
10292 // ============================================================================
10293 // setup file functions
10294 // ============================================================================
10295
10296 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10297
10298
10299 static struct TokenInfo global_setup_tokens[] =
10300 {
10301   {
10302     TYPE_STRING,
10303     &setup.player_name,                         "player_name"
10304   },
10305   {
10306     TYPE_SWITCH,
10307     &setup.multiple_users,                      "multiple_users"
10308   },
10309   {
10310     TYPE_SWITCH,
10311     &setup.sound,                               "sound"
10312   },
10313   {
10314     TYPE_SWITCH,
10315     &setup.sound_loops,                         "repeating_sound_loops"
10316   },
10317   {
10318     TYPE_SWITCH,
10319     &setup.sound_music,                         "background_music"
10320   },
10321   {
10322     TYPE_SWITCH,
10323     &setup.sound_simple,                        "simple_sound_effects"
10324   },
10325   {
10326     TYPE_SWITCH,
10327     &setup.toons,                               "toons"
10328   },
10329   {
10330     TYPE_SWITCH,
10331     &setup.global_animations,                   "global_animations"
10332   },
10333   {
10334     TYPE_SWITCH,
10335     &setup.scroll_delay,                        "scroll_delay"
10336   },
10337   {
10338     TYPE_SWITCH,
10339     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10340   },
10341   {
10342     TYPE_INTEGER,
10343     &setup.scroll_delay_value,                  "scroll_delay_value"
10344   },
10345   {
10346     TYPE_STRING,
10347     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10348   },
10349   {
10350     TYPE_INTEGER,
10351     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10352   },
10353   {
10354     TYPE_SWITCH,
10355     &setup.fade_screens,                        "fade_screens"
10356   },
10357   {
10358     TYPE_SWITCH,
10359     &setup.autorecord,                          "automatic_tape_recording"
10360   },
10361   {
10362     TYPE_SWITCH,
10363     &setup.autorecord_after_replay,             "autorecord_after_replay"
10364   },
10365   {
10366     TYPE_SWITCH,
10367     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10368   },
10369   {
10370     TYPE_SWITCH,
10371     &setup.show_titlescreen,                    "show_titlescreen"
10372   },
10373   {
10374     TYPE_SWITCH,
10375     &setup.quick_doors,                         "quick_doors"
10376   },
10377   {
10378     TYPE_SWITCH,
10379     &setup.team_mode,                           "team_mode"
10380   },
10381   {
10382     TYPE_SWITCH,
10383     &setup.handicap,                            "handicap"
10384   },
10385   {
10386     TYPE_SWITCH,
10387     &setup.skip_levels,                         "skip_levels"
10388   },
10389   {
10390     TYPE_SWITCH,
10391     &setup.increment_levels,                    "increment_levels"
10392   },
10393   {
10394     TYPE_SWITCH,
10395     &setup.auto_play_next_level,                "auto_play_next_level"
10396   },
10397   {
10398     TYPE_SWITCH,
10399     &setup.count_score_after_game,              "count_score_after_game"
10400   },
10401   {
10402     TYPE_SWITCH,
10403     &setup.show_scores_after_game,              "show_scores_after_game"
10404   },
10405   {
10406     TYPE_SWITCH,
10407     &setup.time_limit,                          "time_limit"
10408   },
10409   {
10410     TYPE_SWITCH,
10411     &setup.fullscreen,                          "fullscreen"
10412   },
10413   {
10414     TYPE_INTEGER,
10415     &setup.window_scaling_percent,              "window_scaling_percent"
10416   },
10417   {
10418     TYPE_STRING,
10419     &setup.window_scaling_quality,              "window_scaling_quality"
10420   },
10421   {
10422     TYPE_STRING,
10423     &setup.screen_rendering_mode,               "screen_rendering_mode"
10424   },
10425   {
10426     TYPE_STRING,
10427     &setup.vsync_mode,                          "vsync_mode"
10428   },
10429   {
10430     TYPE_SWITCH,
10431     &setup.ask_on_escape,                       "ask_on_escape"
10432   },
10433   {
10434     TYPE_SWITCH,
10435     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10436   },
10437   {
10438     TYPE_SWITCH,
10439     &setup.ask_on_game_over,                    "ask_on_game_over"
10440   },
10441   {
10442     TYPE_SWITCH,
10443     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10444   },
10445   {
10446     TYPE_SWITCH,
10447     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10448   },
10449   {
10450     TYPE_SWITCH,
10451     &setup.quick_switch,                        "quick_player_switch"
10452   },
10453   {
10454     TYPE_SWITCH,
10455     &setup.input_on_focus,                      "input_on_focus"
10456   },
10457   {
10458     TYPE_SWITCH,
10459     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10460   },
10461   {
10462     TYPE_SWITCH,
10463     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10464   },
10465   {
10466     TYPE_SWITCH,
10467     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10468   },
10469   {
10470     TYPE_SWITCH,
10471     &setup.game_speed_extended,                 "game_speed_extended"
10472   },
10473   {
10474     TYPE_INTEGER,
10475     &setup.game_frame_delay,                    "game_frame_delay"
10476   },
10477   {
10478     TYPE_SWITCH,
10479     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10480   },
10481   {
10482     TYPE_SWITCH,
10483     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10484   },
10485   {
10486     TYPE_SWITCH,
10487     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10488   },
10489   {
10490     TYPE_SWITCH3,
10491     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10492   },
10493   {
10494     TYPE_SWITCH,
10495     &setup.sp_show_border_elements,             "sp_show_border_elements"
10496   },
10497   {
10498     TYPE_SWITCH,
10499     &setup.small_game_graphics,                 "small_game_graphics"
10500   },
10501   {
10502     TYPE_SWITCH,
10503     &setup.show_load_save_buttons,              "show_load_save_buttons"
10504   },
10505   {
10506     TYPE_SWITCH,
10507     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10508   },
10509   {
10510     TYPE_STRING,
10511     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10512   },
10513   {
10514     TYPE_STRING,
10515     &setup.graphics_set,                        "graphics_set"
10516   },
10517   {
10518     TYPE_STRING,
10519     &setup.sounds_set,                          "sounds_set"
10520   },
10521   {
10522     TYPE_STRING,
10523     &setup.music_set,                           "music_set"
10524   },
10525   {
10526     TYPE_SWITCH3,
10527     &setup.override_level_graphics,             "override_level_graphics"
10528   },
10529   {
10530     TYPE_SWITCH3,
10531     &setup.override_level_sounds,               "override_level_sounds"
10532   },
10533   {
10534     TYPE_SWITCH3,
10535     &setup.override_level_music,                "override_level_music"
10536   },
10537   {
10538     TYPE_INTEGER,
10539     &setup.volume_simple,                       "volume_simple"
10540   },
10541   {
10542     TYPE_INTEGER,
10543     &setup.volume_loops,                        "volume_loops"
10544   },
10545   {
10546     TYPE_INTEGER,
10547     &setup.volume_music,                        "volume_music"
10548   },
10549   {
10550     TYPE_SWITCH,
10551     &setup.network_mode,                        "network_mode"
10552   },
10553   {
10554     TYPE_PLAYER,
10555     &setup.network_player_nr,                   "network_player"
10556   },
10557   {
10558     TYPE_STRING,
10559     &setup.network_server_hostname,             "network_server_hostname"
10560   },
10561   {
10562     TYPE_STRING,
10563     &setup.touch.control_type,                  "touch.control_type"
10564   },
10565   {
10566     TYPE_INTEGER,
10567     &setup.touch.move_distance,                 "touch.move_distance"
10568   },
10569   {
10570     TYPE_INTEGER,
10571     &setup.touch.drop_distance,                 "touch.drop_distance"
10572   },
10573   {
10574     TYPE_INTEGER,
10575     &setup.touch.transparency,                  "touch.transparency"
10576   },
10577   {
10578     TYPE_INTEGER,
10579     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10580   },
10581   {
10582     TYPE_INTEGER,
10583     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10584   },
10585   {
10586     TYPE_INTEGER,
10587     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10588   },
10589   {
10590     TYPE_INTEGER,
10591     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10592   },
10593   {
10594     TYPE_INTEGER,
10595     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10596   },
10597   {
10598     TYPE_INTEGER,
10599     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10600   },
10601   {
10602     TYPE_SWITCH,
10603     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10604   },
10605 };
10606
10607 static struct TokenInfo auto_setup_tokens[] =
10608 {
10609   {
10610     TYPE_INTEGER,
10611     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10612   },
10613 };
10614
10615 static struct TokenInfo server_setup_tokens[] =
10616 {
10617   {
10618     TYPE_STRING,
10619     &setup.player_uuid,                         "player_uuid"
10620   },
10621   {
10622     TYPE_INTEGER,
10623     &setup.player_version,                      "player_version"
10624   },
10625   {
10626     TYPE_SWITCH,
10627     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10628   },
10629   {
10630     TYPE_STRING,
10631     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10632   },
10633   {
10634     TYPE_STRING,
10635     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10636   },
10637   {
10638     TYPE_SWITCH,
10639     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10640   },
10641   {
10642     TYPE_SWITCH,
10643     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10644   },
10645   {
10646     TYPE_SWITCH,
10647     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10648   },
10649   {
10650     TYPE_SWITCH,
10651     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10652   },
10653   {
10654     TYPE_SWITCH,
10655     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10656   },
10657 };
10658
10659 static struct TokenInfo editor_setup_tokens[] =
10660 {
10661   {
10662     TYPE_SWITCH,
10663     &setup.editor.el_classic,                   "editor.el_classic"
10664   },
10665   {
10666     TYPE_SWITCH,
10667     &setup.editor.el_custom,                    "editor.el_custom"
10668   },
10669   {
10670     TYPE_SWITCH,
10671     &setup.editor.el_user_defined,              "editor.el_user_defined"
10672   },
10673   {
10674     TYPE_SWITCH,
10675     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10676   },
10677   {
10678     TYPE_SWITCH,
10679     &setup.editor.el_headlines,                 "editor.el_headlines"
10680   },
10681   {
10682     TYPE_SWITCH,
10683     &setup.editor.show_element_token,           "editor.show_element_token"
10684   },
10685   {
10686     TYPE_SWITCH,
10687     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10688   },
10689 };
10690
10691 static struct TokenInfo editor_cascade_setup_tokens[] =
10692 {
10693   {
10694     TYPE_SWITCH,
10695     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10696   },
10697   {
10698     TYPE_SWITCH,
10699     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10700   },
10701   {
10702     TYPE_SWITCH,
10703     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10704   },
10705   {
10706     TYPE_SWITCH,
10707     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10708   },
10709   {
10710     TYPE_SWITCH,
10711     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10712   },
10713   {
10714     TYPE_SWITCH,
10715     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10716   },
10717   {
10718     TYPE_SWITCH,
10719     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10720   },
10721   {
10722     TYPE_SWITCH,
10723     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10724   },
10725   {
10726     TYPE_SWITCH,
10727     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10728   },
10729   {
10730     TYPE_SWITCH,
10731     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10732   },
10733   {
10734     TYPE_SWITCH,
10735     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10736   },
10737   {
10738     TYPE_SWITCH,
10739     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10740   },
10741   {
10742     TYPE_SWITCH,
10743     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10744   },
10745   {
10746     TYPE_SWITCH,
10747     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10748   },
10749   {
10750     TYPE_SWITCH,
10751     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10752   },
10753   {
10754     TYPE_SWITCH,
10755     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10756   },
10757   {
10758     TYPE_SWITCH,
10759     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10760   },
10761   {
10762     TYPE_SWITCH,
10763     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10764   },
10765   {
10766     TYPE_SWITCH,
10767     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10768   },
10769   {
10770     TYPE_SWITCH,
10771     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10772   },
10773 };
10774
10775 static struct TokenInfo shortcut_setup_tokens[] =
10776 {
10777   {
10778     TYPE_KEY_X11,
10779     &setup.shortcut.save_game,                  "shortcut.save_game"
10780   },
10781   {
10782     TYPE_KEY_X11,
10783     &setup.shortcut.load_game,                  "shortcut.load_game"
10784   },
10785   {
10786     TYPE_KEY_X11,
10787     &setup.shortcut.restart_game,               "shortcut.restart_game"
10788   },
10789   {
10790     TYPE_KEY_X11,
10791     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10792   },
10793   {
10794     TYPE_KEY_X11,
10795     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10796   },
10797   {
10798     TYPE_KEY_X11,
10799     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10800   },
10801   {
10802     TYPE_KEY_X11,
10803     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10804   },
10805   {
10806     TYPE_KEY_X11,
10807     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10808   },
10809   {
10810     TYPE_KEY_X11,
10811     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10812   },
10813   {
10814     TYPE_KEY_X11,
10815     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10816   },
10817   {
10818     TYPE_KEY_X11,
10819     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10820   },
10821   {
10822     TYPE_KEY_X11,
10823     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10824   },
10825   {
10826     TYPE_KEY_X11,
10827     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10828   },
10829   {
10830     TYPE_KEY_X11,
10831     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10832   },
10833   {
10834     TYPE_KEY_X11,
10835     &setup.shortcut.tape_record,                "shortcut.tape_record"
10836   },
10837   {
10838     TYPE_KEY_X11,
10839     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10840   },
10841   {
10842     TYPE_KEY_X11,
10843     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10844   },
10845   {
10846     TYPE_KEY_X11,
10847     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10848   },
10849   {
10850     TYPE_KEY_X11,
10851     &setup.shortcut.sound_music,                "shortcut.sound_music"
10852   },
10853   {
10854     TYPE_KEY_X11,
10855     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10856   },
10857   {
10858     TYPE_KEY_X11,
10859     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10860   },
10861   {
10862     TYPE_KEY_X11,
10863     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10864   },
10865   {
10866     TYPE_KEY_X11,
10867     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10868   },
10869 };
10870
10871 static struct SetupInputInfo setup_input;
10872 static struct TokenInfo player_setup_tokens[] =
10873 {
10874   {
10875     TYPE_BOOLEAN,
10876     &setup_input.use_joystick,                  ".use_joystick"
10877   },
10878   {
10879     TYPE_STRING,
10880     &setup_input.joy.device_name,               ".joy.device_name"
10881   },
10882   {
10883     TYPE_INTEGER,
10884     &setup_input.joy.xleft,                     ".joy.xleft"
10885   },
10886   {
10887     TYPE_INTEGER,
10888     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10889   },
10890   {
10891     TYPE_INTEGER,
10892     &setup_input.joy.xright,                    ".joy.xright"
10893   },
10894   {
10895     TYPE_INTEGER,
10896     &setup_input.joy.yupper,                    ".joy.yupper"
10897   },
10898   {
10899     TYPE_INTEGER,
10900     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10901   },
10902   {
10903     TYPE_INTEGER,
10904     &setup_input.joy.ylower,                    ".joy.ylower"
10905   },
10906   {
10907     TYPE_INTEGER,
10908     &setup_input.joy.snap,                      ".joy.snap_field"
10909   },
10910   {
10911     TYPE_INTEGER,
10912     &setup_input.joy.drop,                      ".joy.place_bomb"
10913   },
10914   {
10915     TYPE_KEY_X11,
10916     &setup_input.key.left,                      ".key.move_left"
10917   },
10918   {
10919     TYPE_KEY_X11,
10920     &setup_input.key.right,                     ".key.move_right"
10921   },
10922   {
10923     TYPE_KEY_X11,
10924     &setup_input.key.up,                        ".key.move_up"
10925   },
10926   {
10927     TYPE_KEY_X11,
10928     &setup_input.key.down,                      ".key.move_down"
10929   },
10930   {
10931     TYPE_KEY_X11,
10932     &setup_input.key.snap,                      ".key.snap_field"
10933   },
10934   {
10935     TYPE_KEY_X11,
10936     &setup_input.key.drop,                      ".key.place_bomb"
10937   },
10938 };
10939
10940 static struct TokenInfo system_setup_tokens[] =
10941 {
10942   {
10943     TYPE_STRING,
10944     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10945   },
10946   {
10947     TYPE_STRING,
10948     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10949   },
10950   {
10951     TYPE_STRING,
10952     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10953   },
10954   {
10955     TYPE_INTEGER,
10956     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10957   },
10958 };
10959
10960 static struct TokenInfo internal_setup_tokens[] =
10961 {
10962   {
10963     TYPE_STRING,
10964     &setup.internal.program_title,              "program_title"
10965   },
10966   {
10967     TYPE_STRING,
10968     &setup.internal.program_version,            "program_version"
10969   },
10970   {
10971     TYPE_STRING,
10972     &setup.internal.program_author,             "program_author"
10973   },
10974   {
10975     TYPE_STRING,
10976     &setup.internal.program_email,              "program_email"
10977   },
10978   {
10979     TYPE_STRING,
10980     &setup.internal.program_website,            "program_website"
10981   },
10982   {
10983     TYPE_STRING,
10984     &setup.internal.program_copyright,          "program_copyright"
10985   },
10986   {
10987     TYPE_STRING,
10988     &setup.internal.program_company,            "program_company"
10989   },
10990   {
10991     TYPE_STRING,
10992     &setup.internal.program_icon_file,          "program_icon_file"
10993   },
10994   {
10995     TYPE_STRING,
10996     &setup.internal.default_graphics_set,       "default_graphics_set"
10997   },
10998   {
10999     TYPE_STRING,
11000     &setup.internal.default_sounds_set,         "default_sounds_set"
11001   },
11002   {
11003     TYPE_STRING,
11004     &setup.internal.default_music_set,          "default_music_set"
11005   },
11006   {
11007     TYPE_STRING,
11008     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11009   },
11010   {
11011     TYPE_STRING,
11012     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11013   },
11014   {
11015     TYPE_STRING,
11016     &setup.internal.fallback_music_file,        "fallback_music_file"
11017   },
11018   {
11019     TYPE_STRING,
11020     &setup.internal.default_level_series,       "default_level_series"
11021   },
11022   {
11023     TYPE_INTEGER,
11024     &setup.internal.default_window_width,       "default_window_width"
11025   },
11026   {
11027     TYPE_INTEGER,
11028     &setup.internal.default_window_height,      "default_window_height"
11029   },
11030   {
11031     TYPE_BOOLEAN,
11032     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11033   },
11034   {
11035     TYPE_BOOLEAN,
11036     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11037   },
11038   {
11039     TYPE_BOOLEAN,
11040     &setup.internal.create_user_levelset,       "create_user_levelset"
11041   },
11042   {
11043     TYPE_BOOLEAN,
11044     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11045   },
11046   {
11047     TYPE_BOOLEAN,
11048     &setup.internal.menu_game,                  "menu_game"
11049   },
11050   {
11051     TYPE_BOOLEAN,
11052     &setup.internal.menu_engines,               "menu_engines"
11053   },
11054   {
11055     TYPE_BOOLEAN,
11056     &setup.internal.menu_editor,                "menu_editor"
11057   },
11058   {
11059     TYPE_BOOLEAN,
11060     &setup.internal.menu_graphics,              "menu_graphics"
11061   },
11062   {
11063     TYPE_BOOLEAN,
11064     &setup.internal.menu_sound,                 "menu_sound"
11065   },
11066   {
11067     TYPE_BOOLEAN,
11068     &setup.internal.menu_artwork,               "menu_artwork"
11069   },
11070   {
11071     TYPE_BOOLEAN,
11072     &setup.internal.menu_input,                 "menu_input"
11073   },
11074   {
11075     TYPE_BOOLEAN,
11076     &setup.internal.menu_touch,                 "menu_touch"
11077   },
11078   {
11079     TYPE_BOOLEAN,
11080     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11081   },
11082   {
11083     TYPE_BOOLEAN,
11084     &setup.internal.menu_exit,                  "menu_exit"
11085   },
11086   {
11087     TYPE_BOOLEAN,
11088     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11089   },
11090   {
11091     TYPE_BOOLEAN,
11092     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11093   },
11094   {
11095     TYPE_BOOLEAN,
11096     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11097   },
11098   {
11099     TYPE_BOOLEAN,
11100     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11101   },
11102   {
11103     TYPE_BOOLEAN,
11104     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11105   },
11106   {
11107     TYPE_BOOLEAN,
11108     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11109   },
11110   {
11111     TYPE_BOOLEAN,
11112     &setup.internal.info_title,                 "info_title"
11113   },
11114   {
11115     TYPE_BOOLEAN,
11116     &setup.internal.info_elements,              "info_elements"
11117   },
11118   {
11119     TYPE_BOOLEAN,
11120     &setup.internal.info_music,                 "info_music"
11121   },
11122   {
11123     TYPE_BOOLEAN,
11124     &setup.internal.info_credits,               "info_credits"
11125   },
11126   {
11127     TYPE_BOOLEAN,
11128     &setup.internal.info_program,               "info_program"
11129   },
11130   {
11131     TYPE_BOOLEAN,
11132     &setup.internal.info_version,               "info_version"
11133   },
11134   {
11135     TYPE_BOOLEAN,
11136     &setup.internal.info_levelset,              "info_levelset"
11137   },
11138   {
11139     TYPE_BOOLEAN,
11140     &setup.internal.info_exit,                  "info_exit"
11141   },
11142 };
11143
11144 static struct TokenInfo debug_setup_tokens[] =
11145 {
11146   {
11147     TYPE_INTEGER,
11148     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11149   },
11150   {
11151     TYPE_INTEGER,
11152     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11153   },
11154   {
11155     TYPE_INTEGER,
11156     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11157   },
11158   {
11159     TYPE_INTEGER,
11160     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11161   },
11162   {
11163     TYPE_INTEGER,
11164     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11165   },
11166   {
11167     TYPE_INTEGER,
11168     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11169   },
11170   {
11171     TYPE_INTEGER,
11172     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11173   },
11174   {
11175     TYPE_INTEGER,
11176     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11177   },
11178   {
11179     TYPE_INTEGER,
11180     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11181   },
11182   {
11183     TYPE_INTEGER,
11184     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11185   },
11186   {
11187     TYPE_KEY_X11,
11188     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11189   },
11190   {
11191     TYPE_KEY_X11,
11192     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11193   },
11194   {
11195     TYPE_KEY_X11,
11196     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11197   },
11198   {
11199     TYPE_KEY_X11,
11200     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11201   },
11202   {
11203     TYPE_KEY_X11,
11204     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11205   },
11206   {
11207     TYPE_KEY_X11,
11208     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11209   },
11210   {
11211     TYPE_KEY_X11,
11212     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11213   },
11214   {
11215     TYPE_KEY_X11,
11216     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11217   },
11218   {
11219     TYPE_KEY_X11,
11220     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11221   },
11222   {
11223     TYPE_KEY_X11,
11224     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11225   },
11226   {
11227     TYPE_BOOLEAN,
11228     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11229   {
11230     TYPE_BOOLEAN,
11231     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11232   },
11233   {
11234     TYPE_BOOLEAN,
11235     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11236   },
11237   {
11238     TYPE_SWITCH3,
11239     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11240   },
11241   {
11242     TYPE_INTEGER,
11243     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11244   },
11245 };
11246
11247 static struct TokenInfo options_setup_tokens[] =
11248 {
11249   {
11250     TYPE_BOOLEAN,
11251     &setup.options.verbose,                     "options.verbose"
11252   },
11253   {
11254     TYPE_BOOLEAN,
11255     &setup.options.debug,                       "options.debug"
11256   },
11257   {
11258     TYPE_STRING,
11259     &setup.options.debug_mode,                  "options.debug_mode"
11260   },
11261 };
11262
11263 static void setSetupInfoToDefaults(struct SetupInfo *si)
11264 {
11265   int i;
11266
11267   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11268
11269   si->multiple_users = TRUE;
11270
11271   si->sound = TRUE;
11272   si->sound_loops = TRUE;
11273   si->sound_music = TRUE;
11274   si->sound_simple = TRUE;
11275   si->toons = TRUE;
11276   si->global_animations = TRUE;
11277   si->scroll_delay = TRUE;
11278   si->forced_scroll_delay = FALSE;
11279   si->scroll_delay_value = STD_SCROLL_DELAY;
11280   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11281   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11282   si->fade_screens = TRUE;
11283   si->autorecord = TRUE;
11284   si->autorecord_after_replay = TRUE;
11285   si->auto_pause_on_start = FALSE;
11286   si->show_titlescreen = TRUE;
11287   si->quick_doors = FALSE;
11288   si->team_mode = FALSE;
11289   si->handicap = TRUE;
11290   si->skip_levels = TRUE;
11291   si->increment_levels = TRUE;
11292   si->auto_play_next_level = TRUE;
11293   si->count_score_after_game = TRUE;
11294   si->show_scores_after_game = TRUE;
11295   si->time_limit = TRUE;
11296   si->fullscreen = FALSE;
11297   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11298   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11299   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11300   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11301   si->ask_on_escape = TRUE;
11302   si->ask_on_escape_editor = TRUE;
11303   si->ask_on_game_over = TRUE;
11304   si->ask_on_quit_game = TRUE;
11305   si->ask_on_quit_program = TRUE;
11306   si->quick_switch = FALSE;
11307   si->input_on_focus = FALSE;
11308   si->prefer_aga_graphics = TRUE;
11309   si->prefer_lowpass_sounds = FALSE;
11310   si->prefer_extra_panel_items = TRUE;
11311   si->game_speed_extended = FALSE;
11312   si->game_frame_delay = GAME_FRAME_DELAY;
11313   si->bd_skip_uncovering = FALSE;
11314   si->bd_skip_hatching = FALSE;
11315   si->bd_scroll_delay = TRUE;
11316   si->bd_smooth_movements = AUTO;
11317   si->sp_show_border_elements = FALSE;
11318   si->small_game_graphics = FALSE;
11319   si->show_load_save_buttons = FALSE;
11320   si->show_undo_redo_buttons = FALSE;
11321   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11322
11323   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11324   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11325   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11326
11327   si->override_level_graphics = FALSE;
11328   si->override_level_sounds = FALSE;
11329   si->override_level_music = FALSE;
11330
11331   si->volume_simple = 100;              // percent
11332   si->volume_loops = 100;               // percent
11333   si->volume_music = 100;               // percent
11334
11335   si->network_mode = FALSE;
11336   si->network_player_nr = 0;            // first player
11337   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11338
11339   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11340   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11341   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11342   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11343   si->touch.draw_outlined = TRUE;
11344   si->touch.draw_pressed = TRUE;
11345
11346   for (i = 0; i < 2; i++)
11347   {
11348     char *default_grid_button[6][2] =
11349     {
11350       { "      ", "  ^^  " },
11351       { "      ", "  ^^  " },
11352       { "      ", "<<  >>" },
11353       { "      ", "<<  >>" },
11354       { "111222", "  vv  " },
11355       { "111222", "  vv  " }
11356     };
11357     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11358     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11359     int min_xsize = MIN(6, grid_xsize);
11360     int min_ysize = MIN(6, grid_ysize);
11361     int startx = grid_xsize - min_xsize;
11362     int starty = grid_ysize - min_ysize;
11363     int x, y;
11364
11365     // virtual buttons grid can only be set to defaults if video is initialized
11366     // (this will be repeated if virtual buttons are not loaded from setup file)
11367     if (video.initialized)
11368     {
11369       si->touch.grid_xsize[i] = grid_xsize;
11370       si->touch.grid_ysize[i] = grid_ysize;
11371     }
11372     else
11373     {
11374       si->touch.grid_xsize[i] = -1;
11375       si->touch.grid_ysize[i] = -1;
11376     }
11377
11378     for (x = 0; x < MAX_GRID_XSIZE; x++)
11379       for (y = 0; y < MAX_GRID_YSIZE; y++)
11380         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11381
11382     for (x = 0; x < min_xsize; x++)
11383       for (y = 0; y < min_ysize; y++)
11384         si->touch.grid_button[i][x][starty + y] =
11385           default_grid_button[y][0][x];
11386
11387     for (x = 0; x < min_xsize; x++)
11388       for (y = 0; y < min_ysize; y++)
11389         si->touch.grid_button[i][startx + x][starty + y] =
11390           default_grid_button[y][1][x];
11391   }
11392
11393   si->touch.grid_initialized            = video.initialized;
11394
11395   si->touch.overlay_buttons             = FALSE;
11396
11397   si->editor.el_boulderdash             = TRUE;
11398   si->editor.el_boulderdash_native      = TRUE;
11399   si->editor.el_boulderdash_effects     = TRUE;
11400   si->editor.el_emerald_mine            = TRUE;
11401   si->editor.el_emerald_mine_club       = TRUE;
11402   si->editor.el_more                    = TRUE;
11403   si->editor.el_sokoban                 = TRUE;
11404   si->editor.el_supaplex                = TRUE;
11405   si->editor.el_diamond_caves           = TRUE;
11406   si->editor.el_dx_boulderdash          = TRUE;
11407
11408   si->editor.el_mirror_magic            = TRUE;
11409   si->editor.el_deflektor               = TRUE;
11410
11411   si->editor.el_chars                   = TRUE;
11412   si->editor.el_steel_chars             = TRUE;
11413
11414   si->editor.el_classic                 = TRUE;
11415   si->editor.el_custom                  = TRUE;
11416
11417   si->editor.el_user_defined            = FALSE;
11418   si->editor.el_dynamic                 = TRUE;
11419
11420   si->editor.el_headlines               = TRUE;
11421
11422   si->editor.show_element_token         = FALSE;
11423
11424   si->editor.show_read_only_warning     = TRUE;
11425
11426   si->editor.use_template_for_new_levels = TRUE;
11427
11428   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11429   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11430   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11431   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11432   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11433
11434   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11435   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11436   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11437   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11438   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11439
11440   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11441   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11442   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11443   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11444   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11445   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11446
11447   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11448   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11449   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11450
11451   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11452   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11453   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11454   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11455
11456   for (i = 0; i < MAX_PLAYERS; i++)
11457   {
11458     si->input[i].use_joystick = FALSE;
11459     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11460     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11461     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11462     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11463     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11464     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11465     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11466     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11467     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11468     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11469     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11470     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11471     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11472     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11473     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11474   }
11475
11476   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11477   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11478   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11479   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11480
11481   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11482   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11483   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11484   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11485   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11486   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11487   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11488
11489   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11490
11491   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11492   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11493   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11494
11495   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11496   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11497   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11498
11499   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11500   si->internal.choose_from_top_leveldir = FALSE;
11501   si->internal.show_scaling_in_title = TRUE;
11502   si->internal.create_user_levelset = TRUE;
11503   si->internal.info_screens_from_main = FALSE;
11504
11505   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11506   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11507
11508   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11509   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11510   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11511   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11512   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11513   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11514   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11515   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11516   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11517   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11518
11519   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11520   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11521   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11522   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11523   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11524   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11525   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11526   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11527   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11528   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11529
11530   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11531   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11532
11533   si->debug.show_frames_per_second = FALSE;
11534
11535   si->debug.xsn_mode = AUTO;
11536   si->debug.xsn_percent = 0;
11537
11538   si->options.verbose = FALSE;
11539   si->options.debug = FALSE;
11540   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11541
11542 #if defined(PLATFORM_ANDROID)
11543   si->fullscreen = TRUE;
11544   si->touch.overlay_buttons = TRUE;
11545 #endif
11546
11547   setHideSetupEntry(&setup.debug.xsn_mode);
11548 }
11549
11550 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11551 {
11552   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11553 }
11554
11555 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11556 {
11557   si->player_uuid = NULL;       // (will be set later)
11558   si->player_version = 1;       // (will be set later)
11559
11560   si->use_api_server = TRUE;
11561   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11562   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11563   si->ask_for_uploading_tapes = TRUE;
11564   si->ask_for_remaining_tapes = FALSE;
11565   si->provide_uploading_tapes = TRUE;
11566   si->ask_for_using_api_server = TRUE;
11567   si->has_remaining_tapes = FALSE;
11568 }
11569
11570 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11571 {
11572   si->editor_cascade.el_bd              = TRUE;
11573   si->editor_cascade.el_bd_native       = TRUE;
11574   si->editor_cascade.el_bd_effects      = FALSE;
11575   si->editor_cascade.el_em              = TRUE;
11576   si->editor_cascade.el_emc             = TRUE;
11577   si->editor_cascade.el_rnd             = TRUE;
11578   si->editor_cascade.el_sb              = TRUE;
11579   si->editor_cascade.el_sp              = TRUE;
11580   si->editor_cascade.el_dc              = TRUE;
11581   si->editor_cascade.el_dx              = TRUE;
11582
11583   si->editor_cascade.el_mm              = TRUE;
11584   si->editor_cascade.el_df              = TRUE;
11585
11586   si->editor_cascade.el_chars           = FALSE;
11587   si->editor_cascade.el_steel_chars     = FALSE;
11588   si->editor_cascade.el_ce              = FALSE;
11589   si->editor_cascade.el_ge              = FALSE;
11590   si->editor_cascade.el_es              = FALSE;
11591   si->editor_cascade.el_ref             = FALSE;
11592   si->editor_cascade.el_user            = FALSE;
11593   si->editor_cascade.el_dynamic         = FALSE;
11594 }
11595
11596 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11597
11598 static char *getHideSetupToken(void *setup_value)
11599 {
11600   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11601
11602   if (setup_value != NULL)
11603     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11604
11605   return hide_setup_token;
11606 }
11607
11608 void setHideSetupEntry(void *setup_value)
11609 {
11610   char *hide_setup_token = getHideSetupToken(setup_value);
11611
11612   if (hide_setup_hash == NULL)
11613     hide_setup_hash = newSetupFileHash();
11614
11615   if (setup_value != NULL)
11616     setHashEntry(hide_setup_hash, hide_setup_token, "");
11617 }
11618
11619 void removeHideSetupEntry(void *setup_value)
11620 {
11621   char *hide_setup_token = getHideSetupToken(setup_value);
11622
11623   if (setup_value != NULL)
11624     removeHashEntry(hide_setup_hash, hide_setup_token);
11625 }
11626
11627 boolean hideSetupEntry(void *setup_value)
11628 {
11629   char *hide_setup_token = getHideSetupToken(setup_value);
11630
11631   return (setup_value != NULL &&
11632           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11633 }
11634
11635 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11636                                       struct TokenInfo *token_info,
11637                                       int token_nr, char *token_text)
11638 {
11639   char *token_hide_text = getStringCat2(token_text, ".hide");
11640   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11641
11642   // set the value of this setup option in the setup option structure
11643   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11644
11645   // check if this setup option should be hidden in the setup menu
11646   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11647     setHideSetupEntry(token_info[token_nr].value);
11648
11649   free(token_hide_text);
11650 }
11651
11652 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11653                                       struct TokenInfo *token_info,
11654                                       int token_nr)
11655 {
11656   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11657                             token_info[token_nr].text);
11658 }
11659
11660 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11661 {
11662   int i, pnr;
11663
11664   if (!setup_file_hash)
11665     return;
11666
11667   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11668     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11669
11670   setup.touch.grid_initialized = TRUE;
11671   for (i = 0; i < 2; i++)
11672   {
11673     int grid_xsize = setup.touch.grid_xsize[i];
11674     int grid_ysize = setup.touch.grid_ysize[i];
11675     int x, y;
11676
11677     // if virtual buttons are not loaded from setup file, repeat initializing
11678     // virtual buttons grid with default values later when video is initialized
11679     if (grid_xsize == -1 ||
11680         grid_ysize == -1)
11681     {
11682       setup.touch.grid_initialized = FALSE;
11683
11684       continue;
11685     }
11686
11687     for (y = 0; y < grid_ysize; y++)
11688     {
11689       char token_string[MAX_LINE_LEN];
11690
11691       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11692
11693       char *value_string = getHashEntry(setup_file_hash, token_string);
11694
11695       if (value_string == NULL)
11696         continue;
11697
11698       for (x = 0; x < grid_xsize; x++)
11699       {
11700         char c = value_string[x];
11701
11702         setup.touch.grid_button[i][x][y] =
11703           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11704       }
11705     }
11706   }
11707
11708   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11709     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11710
11711   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11712     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11713
11714   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11715   {
11716     char prefix[30];
11717
11718     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11719
11720     setup_input = setup.input[pnr];
11721     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11722     {
11723       char full_token[100];
11724
11725       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11726       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11727                                 full_token);
11728     }
11729     setup.input[pnr] = setup_input;
11730   }
11731
11732   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11733     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11734
11735   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11736     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11737
11738   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11739     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11740
11741   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11742     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11743
11744   setHideRelatedSetupEntries();
11745 }
11746
11747 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11748 {
11749   int i;
11750
11751   if (!setup_file_hash)
11752     return;
11753
11754   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11755     setSetupInfo(auto_setup_tokens, i,
11756                  getHashEntry(setup_file_hash,
11757                               auto_setup_tokens[i].text));
11758 }
11759
11760 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11761 {
11762   int i;
11763
11764   if (!setup_file_hash)
11765     return;
11766
11767   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11768     setSetupInfo(server_setup_tokens, i,
11769                  getHashEntry(setup_file_hash,
11770                               server_setup_tokens[i].text));
11771 }
11772
11773 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11774 {
11775   int i;
11776
11777   if (!setup_file_hash)
11778     return;
11779
11780   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11781     setSetupInfo(editor_cascade_setup_tokens, i,
11782                  getHashEntry(setup_file_hash,
11783                               editor_cascade_setup_tokens[i].text));
11784 }
11785
11786 void LoadUserNames(void)
11787 {
11788   int last_user_nr = user.nr;
11789   int i;
11790
11791   if (global.user_names != NULL)
11792   {
11793     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11794       checked_free(global.user_names[i]);
11795
11796     checked_free(global.user_names);
11797   }
11798
11799   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11800
11801   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11802   {
11803     user.nr = i;
11804
11805     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11806
11807     if (setup_file_hash)
11808     {
11809       char *player_name = getHashEntry(setup_file_hash, "player_name");
11810
11811       global.user_names[i] = getFixedUserName(player_name);
11812
11813       freeSetupFileHash(setup_file_hash);
11814     }
11815
11816     if (global.user_names[i] == NULL)
11817       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11818   }
11819
11820   user.nr = last_user_nr;
11821 }
11822
11823 void LoadSetupFromFilename(char *filename)
11824 {
11825   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11826
11827   if (setup_file_hash)
11828   {
11829     decodeSetupFileHash_Default(setup_file_hash);
11830
11831     freeSetupFileHash(setup_file_hash);
11832   }
11833   else
11834   {
11835     Debug("setup", "using default setup values");
11836   }
11837 }
11838
11839 static void LoadSetup_SpecialPostProcessing(void)
11840 {
11841   char *player_name_new;
11842
11843   // needed to work around problems with fixed length strings
11844   player_name_new = getFixedUserName(setup.player_name);
11845   free(setup.player_name);
11846   setup.player_name = player_name_new;
11847
11848   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11849   if (setup.scroll_delay == FALSE)
11850   {
11851     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11852     setup.scroll_delay = TRUE;                  // now always "on"
11853   }
11854
11855   // make sure that scroll delay value stays inside valid range
11856   setup.scroll_delay_value =
11857     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11858 }
11859
11860 void LoadSetup_Default(void)
11861 {
11862   char *filename;
11863
11864   // always start with reliable default values
11865   setSetupInfoToDefaults(&setup);
11866
11867   // try to load setup values from default setup file
11868   filename = getDefaultSetupFilename();
11869
11870   if (fileExists(filename))
11871     LoadSetupFromFilename(filename);
11872
11873   // try to load setup values from platform setup file
11874   filename = getPlatformSetupFilename();
11875
11876   if (fileExists(filename))
11877     LoadSetupFromFilename(filename);
11878
11879   // try to load setup values from user setup file
11880   filename = getSetupFilename();
11881
11882   LoadSetupFromFilename(filename);
11883
11884   LoadSetup_SpecialPostProcessing();
11885 }
11886
11887 void LoadSetup_AutoSetup(void)
11888 {
11889   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11890   SetupFileHash *setup_file_hash = NULL;
11891
11892   // always start with reliable default values
11893   setSetupInfoToDefaults_AutoSetup(&setup);
11894
11895   setup_file_hash = loadSetupFileHash(filename);
11896
11897   if (setup_file_hash)
11898   {
11899     decodeSetupFileHash_AutoSetup(setup_file_hash);
11900
11901     freeSetupFileHash(setup_file_hash);
11902   }
11903
11904   free(filename);
11905 }
11906
11907 void LoadSetup_ServerSetup(void)
11908 {
11909   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11910   SetupFileHash *setup_file_hash = NULL;
11911
11912   // always start with reliable default values
11913   setSetupInfoToDefaults_ServerSetup(&setup);
11914
11915   setup_file_hash = loadSetupFileHash(filename);
11916
11917   if (setup_file_hash)
11918   {
11919     decodeSetupFileHash_ServerSetup(setup_file_hash);
11920
11921     freeSetupFileHash(setup_file_hash);
11922   }
11923
11924   free(filename);
11925
11926   if (setup.player_uuid == NULL)
11927   {
11928     // player UUID does not yet exist in setup file
11929     setup.player_uuid = getStringCopy(getUUID());
11930     setup.player_version = 2;
11931
11932     SaveSetup_ServerSetup();
11933   }
11934 }
11935
11936 void LoadSetup_EditorCascade(void)
11937 {
11938   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11939   SetupFileHash *setup_file_hash = NULL;
11940
11941   // always start with reliable default values
11942   setSetupInfoToDefaults_EditorCascade(&setup);
11943
11944   setup_file_hash = loadSetupFileHash(filename);
11945
11946   if (setup_file_hash)
11947   {
11948     decodeSetupFileHash_EditorCascade(setup_file_hash);
11949
11950     freeSetupFileHash(setup_file_hash);
11951   }
11952
11953   free(filename);
11954 }
11955
11956 void LoadSetup(void)
11957 {
11958   LoadSetup_Default();
11959   LoadSetup_AutoSetup();
11960   LoadSetup_ServerSetup();
11961   LoadSetup_EditorCascade();
11962 }
11963
11964 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11965                                            char *mapping_line)
11966 {
11967   char mapping_guid[MAX_LINE_LEN];
11968   char *mapping_start, *mapping_end;
11969
11970   // get GUID from game controller mapping line: copy complete line
11971   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11972   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11973
11974   // get GUID from game controller mapping line: cut after GUID part
11975   mapping_start = strchr(mapping_guid, ',');
11976   if (mapping_start != NULL)
11977     *mapping_start = '\0';
11978
11979   // cut newline from game controller mapping line
11980   mapping_end = strchr(mapping_line, '\n');
11981   if (mapping_end != NULL)
11982     *mapping_end = '\0';
11983
11984   // add mapping entry to game controller mappings hash
11985   setHashEntry(mappings_hash, mapping_guid, mapping_line);
11986 }
11987
11988 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
11989                                                  char *filename)
11990 {
11991   FILE *file;
11992
11993   if (!(file = fopen(filename, MODE_READ)))
11994   {
11995     Warn("cannot read game controller mappings file '%s'", filename);
11996
11997     return;
11998   }
11999
12000   while (!feof(file))
12001   {
12002     char line[MAX_LINE_LEN];
12003
12004     if (!fgets(line, MAX_LINE_LEN, file))
12005       break;
12006
12007     addGameControllerMappingToHash(mappings_hash, line);
12008   }
12009
12010   fclose(file);
12011 }
12012
12013 void SaveSetup_Default(void)
12014 {
12015   char *filename = getSetupFilename();
12016   FILE *file;
12017   int i, pnr;
12018
12019   InitUserDataDirectory();
12020
12021   if (!(file = fopen(filename, MODE_WRITE)))
12022   {
12023     Warn("cannot write setup file '%s'", filename);
12024
12025     return;
12026   }
12027
12028   fprintFileHeader(file, SETUP_FILENAME);
12029
12030   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12031   {
12032     // just to make things nicer :)
12033     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12034         global_setup_tokens[i].value == &setup.sound                    ||
12035         global_setup_tokens[i].value == &setup.graphics_set             ||
12036         global_setup_tokens[i].value == &setup.volume_simple            ||
12037         global_setup_tokens[i].value == &setup.network_mode             ||
12038         global_setup_tokens[i].value == &setup.touch.control_type       ||
12039         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12040         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12041       fprintf(file, "\n");
12042
12043     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12044   }
12045
12046   for (i = 0; i < 2; i++)
12047   {
12048     int grid_xsize = setup.touch.grid_xsize[i];
12049     int grid_ysize = setup.touch.grid_ysize[i];
12050     int x, y;
12051
12052     fprintf(file, "\n");
12053
12054     for (y = 0; y < grid_ysize; y++)
12055     {
12056       char token_string[MAX_LINE_LEN];
12057       char value_string[MAX_LINE_LEN];
12058
12059       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12060
12061       for (x = 0; x < grid_xsize; x++)
12062       {
12063         char c = setup.touch.grid_button[i][x][y];
12064
12065         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12066       }
12067
12068       value_string[grid_xsize] = '\0';
12069
12070       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12071     }
12072   }
12073
12074   fprintf(file, "\n");
12075   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12076     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12077
12078   fprintf(file, "\n");
12079   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12080     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12081
12082   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12083   {
12084     char prefix[30];
12085
12086     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12087     fprintf(file, "\n");
12088
12089     setup_input = setup.input[pnr];
12090     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12091       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12092   }
12093
12094   fprintf(file, "\n");
12095   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12096     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12097
12098   // (internal setup values not saved to user setup file)
12099
12100   fprintf(file, "\n");
12101   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12102     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12103         setup.debug.xsn_mode != AUTO)
12104       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12105
12106   fprintf(file, "\n");
12107   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12108     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12109
12110   fclose(file);
12111
12112   SetFilePermissions(filename, PERMS_PRIVATE);
12113 }
12114
12115 void SaveSetup_AutoSetup(void)
12116 {
12117   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12118   FILE *file;
12119   int i;
12120
12121   InitUserDataDirectory();
12122
12123   if (!(file = fopen(filename, MODE_WRITE)))
12124   {
12125     Warn("cannot write auto setup file '%s'", filename);
12126
12127     free(filename);
12128
12129     return;
12130   }
12131
12132   fprintFileHeader(file, AUTOSETUP_FILENAME);
12133
12134   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12135     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12136
12137   fclose(file);
12138
12139   SetFilePermissions(filename, PERMS_PRIVATE);
12140
12141   free(filename);
12142 }
12143
12144 void SaveSetup_ServerSetup(void)
12145 {
12146   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12147   FILE *file;
12148   int i;
12149
12150   InitUserDataDirectory();
12151
12152   if (!(file = fopen(filename, MODE_WRITE)))
12153   {
12154     Warn("cannot write server setup file '%s'", filename);
12155
12156     free(filename);
12157
12158     return;
12159   }
12160
12161   fprintFileHeader(file, SERVERSETUP_FILENAME);
12162
12163   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12164   {
12165     // just to make things nicer :)
12166     if (server_setup_tokens[i].value == &setup.use_api_server)
12167       fprintf(file, "\n");
12168
12169     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12170   }
12171
12172   fclose(file);
12173
12174   SetFilePermissions(filename, PERMS_PRIVATE);
12175
12176   free(filename);
12177 }
12178
12179 void SaveSetup_EditorCascade(void)
12180 {
12181   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12182   FILE *file;
12183   int i;
12184
12185   InitUserDataDirectory();
12186
12187   if (!(file = fopen(filename, MODE_WRITE)))
12188   {
12189     Warn("cannot write editor cascade state file '%s'", filename);
12190
12191     free(filename);
12192
12193     return;
12194   }
12195
12196   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12197
12198   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12199     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12200
12201   fclose(file);
12202
12203   SetFilePermissions(filename, PERMS_PRIVATE);
12204
12205   free(filename);
12206 }
12207
12208 void SaveSetup(void)
12209 {
12210   SaveSetup_Default();
12211   SaveSetup_AutoSetup();
12212   SaveSetup_ServerSetup();
12213   SaveSetup_EditorCascade();
12214 }
12215
12216 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12217                                                   char *filename)
12218 {
12219   FILE *file;
12220
12221   if (!(file = fopen(filename, MODE_WRITE)))
12222   {
12223     Warn("cannot write game controller mappings file '%s'", filename);
12224
12225     return;
12226   }
12227
12228   BEGIN_HASH_ITERATION(mappings_hash, itr)
12229   {
12230     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12231   }
12232   END_HASH_ITERATION(mappings_hash, itr)
12233
12234   fclose(file);
12235 }
12236
12237 void SaveSetup_AddGameControllerMapping(char *mapping)
12238 {
12239   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12240   SetupFileHash *mappings_hash = newSetupFileHash();
12241
12242   InitUserDataDirectory();
12243
12244   // load existing personal game controller mappings
12245   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12246
12247   // add new mapping to personal game controller mappings
12248   addGameControllerMappingToHash(mappings_hash, mapping);
12249
12250   // save updated personal game controller mappings
12251   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12252
12253   freeSetupFileHash(mappings_hash);
12254   free(filename);
12255 }
12256
12257 void LoadCustomElementDescriptions(void)
12258 {
12259   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12260   SetupFileHash *setup_file_hash;
12261   int i;
12262
12263   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12264   {
12265     if (element_info[i].custom_description != NULL)
12266     {
12267       free(element_info[i].custom_description);
12268       element_info[i].custom_description = NULL;
12269     }
12270   }
12271
12272   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12273     return;
12274
12275   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12276   {
12277     char *token = getStringCat2(element_info[i].token_name, ".name");
12278     char *value = getHashEntry(setup_file_hash, token);
12279
12280     if (value != NULL)
12281       element_info[i].custom_description = getStringCopy(value);
12282
12283     free(token);
12284   }
12285
12286   freeSetupFileHash(setup_file_hash);
12287 }
12288
12289 static int getElementFromToken(char *token)
12290 {
12291   char *value = getHashEntry(element_token_hash, token);
12292
12293   if (value != NULL)
12294     return atoi(value);
12295
12296   Warn("unknown element token '%s'", token);
12297
12298   return EL_UNDEFINED;
12299 }
12300
12301 void FreeGlobalAnimEventInfo(void)
12302 {
12303   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12304
12305   if (gaei->event_list == NULL)
12306     return;
12307
12308   int i;
12309
12310   for (i = 0; i < gaei->num_event_lists; i++)
12311   {
12312     checked_free(gaei->event_list[i]->event_value);
12313     checked_free(gaei->event_list[i]);
12314   }
12315
12316   checked_free(gaei->event_list);
12317
12318   gaei->event_list = NULL;
12319   gaei->num_event_lists = 0;
12320 }
12321
12322 static int AddGlobalAnimEventList(void)
12323 {
12324   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12325   int list_pos = gaei->num_event_lists++;
12326
12327   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12328                                      sizeof(struct GlobalAnimEventListInfo *));
12329
12330   gaei->event_list[list_pos] =
12331     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12332
12333   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12334
12335   gaeli->event_value = NULL;
12336   gaeli->num_event_values = 0;
12337
12338   return list_pos;
12339 }
12340
12341 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12342 {
12343   // do not add empty global animation events
12344   if (event_value == ANIM_EVENT_NONE)
12345     return list_pos;
12346
12347   // if list position is undefined, create new list
12348   if (list_pos == ANIM_EVENT_UNDEFINED)
12349     list_pos = AddGlobalAnimEventList();
12350
12351   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12352   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12353   int value_pos = gaeli->num_event_values++;
12354
12355   gaeli->event_value = checked_realloc(gaeli->event_value,
12356                                        gaeli->num_event_values * sizeof(int *));
12357
12358   gaeli->event_value[value_pos] = event_value;
12359
12360   return list_pos;
12361 }
12362
12363 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12364 {
12365   if (list_pos == ANIM_EVENT_UNDEFINED)
12366     return ANIM_EVENT_NONE;
12367
12368   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12369   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12370
12371   return gaeli->event_value[value_pos];
12372 }
12373
12374 int GetGlobalAnimEventValueCount(int list_pos)
12375 {
12376   if (list_pos == ANIM_EVENT_UNDEFINED)
12377     return 0;
12378
12379   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12380   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12381
12382   return gaeli->num_event_values;
12383 }
12384
12385 // This function checks if a string <s> of the format "string1, string2, ..."
12386 // exactly contains a string <s_contained>.
12387
12388 static boolean string_has_parameter(char *s, char *s_contained)
12389 {
12390   char *substring;
12391
12392   if (s == NULL || s_contained == NULL)
12393     return FALSE;
12394
12395   if (strlen(s_contained) > strlen(s))
12396     return FALSE;
12397
12398   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12399   {
12400     char next_char = s[strlen(s_contained)];
12401
12402     // check if next character is delimiter or whitespace
12403     if (next_char == ',' || next_char == '\0' ||
12404         next_char == ' ' || next_char == '\t')
12405       return TRUE;
12406   }
12407
12408   // check if string contains another parameter string after a comma
12409   substring = strchr(s, ',');
12410   if (substring == NULL)        // string does not contain a comma
12411     return FALSE;
12412
12413   // advance string pointer to next character after the comma
12414   substring++;
12415
12416   // skip potential whitespaces after the comma
12417   while (*substring == ' ' || *substring == '\t')
12418     substring++;
12419
12420   return string_has_parameter(substring, s_contained);
12421 }
12422
12423 static int get_anim_parameter_value_ce(char *s)
12424 {
12425   char *s_ptr = s;
12426   char *pattern_1 = "ce_change:custom_";
12427   char *pattern_2 = ".page_";
12428   int pattern_1_len = strlen(pattern_1);
12429   char *matching_char = strstr(s_ptr, pattern_1);
12430   int result = ANIM_EVENT_NONE;
12431
12432   if (matching_char == NULL)
12433     return ANIM_EVENT_NONE;
12434
12435   result = ANIM_EVENT_CE_CHANGE;
12436
12437   s_ptr = matching_char + pattern_1_len;
12438
12439   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12440   if (*s_ptr >= '0' && *s_ptr <= '9')
12441   {
12442     int gic_ce_nr = (*s_ptr++ - '0');
12443
12444     if (*s_ptr >= '0' && *s_ptr <= '9')
12445     {
12446       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12447
12448       if (*s_ptr >= '0' && *s_ptr <= '9')
12449         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12450     }
12451
12452     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12453       return ANIM_EVENT_NONE;
12454
12455     // custom element stored as 0 to 255
12456     gic_ce_nr--;
12457
12458     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12459   }
12460   else
12461   {
12462     // invalid custom element number specified
12463
12464     return ANIM_EVENT_NONE;
12465   }
12466
12467   // check for change page number ("page_X" or "page_XX") (optional)
12468   if (strPrefix(s_ptr, pattern_2))
12469   {
12470     s_ptr += strlen(pattern_2);
12471
12472     if (*s_ptr >= '0' && *s_ptr <= '9')
12473     {
12474       int gic_page_nr = (*s_ptr++ - '0');
12475
12476       if (*s_ptr >= '0' && *s_ptr <= '9')
12477         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12478
12479       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12480         return ANIM_EVENT_NONE;
12481
12482       // change page stored as 1 to 32 (0 means "all change pages")
12483
12484       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12485     }
12486     else
12487     {
12488       // invalid animation part number specified
12489
12490       return ANIM_EVENT_NONE;
12491     }
12492   }
12493
12494   // discard result if next character is neither delimiter nor whitespace
12495   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12496         *s_ptr == ' ' || *s_ptr == '\t'))
12497     return ANIM_EVENT_NONE;
12498
12499   return result;
12500 }
12501
12502 static int get_anim_parameter_value(char *s)
12503 {
12504   int event_value[] =
12505   {
12506     ANIM_EVENT_CLICK,
12507     ANIM_EVENT_INIT,
12508     ANIM_EVENT_START,
12509     ANIM_EVENT_END,
12510     ANIM_EVENT_POST
12511   };
12512   char *pattern_1[] =
12513   {
12514     "click:anim_",
12515     "init:anim_",
12516     "start:anim_",
12517     "end:anim_",
12518     "post:anim_"
12519   };
12520   char *pattern_2 = ".part_";
12521   char *matching_char = NULL;
12522   char *s_ptr = s;
12523   int pattern_1_len = 0;
12524   int result = ANIM_EVENT_NONE;
12525   int i;
12526
12527   result = get_anim_parameter_value_ce(s);
12528
12529   if (result != ANIM_EVENT_NONE)
12530     return result;
12531
12532   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12533   {
12534     matching_char = strstr(s_ptr, pattern_1[i]);
12535     pattern_1_len = strlen(pattern_1[i]);
12536     result = event_value[i];
12537
12538     if (matching_char != NULL)
12539       break;
12540   }
12541
12542   if (matching_char == NULL)
12543     return ANIM_EVENT_NONE;
12544
12545   s_ptr = matching_char + pattern_1_len;
12546
12547   // check for main animation number ("anim_X" or "anim_XX")
12548   if (*s_ptr >= '0' && *s_ptr <= '9')
12549   {
12550     int gic_anim_nr = (*s_ptr++ - '0');
12551
12552     if (*s_ptr >= '0' && *s_ptr <= '9')
12553       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12554
12555     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12556       return ANIM_EVENT_NONE;
12557
12558     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12559   }
12560   else
12561   {
12562     // invalid main animation number specified
12563
12564     return ANIM_EVENT_NONE;
12565   }
12566
12567   // check for animation part number ("part_X" or "part_XX") (optional)
12568   if (strPrefix(s_ptr, pattern_2))
12569   {
12570     s_ptr += strlen(pattern_2);
12571
12572     if (*s_ptr >= '0' && *s_ptr <= '9')
12573     {
12574       int gic_part_nr = (*s_ptr++ - '0');
12575
12576       if (*s_ptr >= '0' && *s_ptr <= '9')
12577         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12578
12579       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12580         return ANIM_EVENT_NONE;
12581
12582       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12583     }
12584     else
12585     {
12586       // invalid animation part number specified
12587
12588       return ANIM_EVENT_NONE;
12589     }
12590   }
12591
12592   // discard result if next character is neither delimiter nor whitespace
12593   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12594         *s_ptr == ' ' || *s_ptr == '\t'))
12595     return ANIM_EVENT_NONE;
12596
12597   return result;
12598 }
12599
12600 static int get_anim_parameter_values(char *s)
12601 {
12602   int list_pos = ANIM_EVENT_UNDEFINED;
12603   int event_value = ANIM_EVENT_DEFAULT;
12604
12605   if (string_has_parameter(s, "any"))
12606     event_value |= ANIM_EVENT_ANY;
12607
12608   if (string_has_parameter(s, "click:self") ||
12609       string_has_parameter(s, "click") ||
12610       string_has_parameter(s, "self"))
12611     event_value |= ANIM_EVENT_SELF;
12612
12613   if (string_has_parameter(s, "unclick:any"))
12614     event_value |= ANIM_EVENT_UNCLICK_ANY;
12615
12616   // if animation event found, add it to global animation event list
12617   if (event_value != ANIM_EVENT_NONE)
12618     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12619
12620   while (s != NULL)
12621   {
12622     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12623     event_value = get_anim_parameter_value(s);
12624
12625     // if animation event found, add it to global animation event list
12626     if (event_value != ANIM_EVENT_NONE)
12627       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12628
12629     // continue with next part of the string, starting with next comma
12630     s = strchr(s + 1, ',');
12631   }
12632
12633   return list_pos;
12634 }
12635
12636 static int get_anim_action_parameter_value(char *token)
12637 {
12638   // check most common default case first to massively speed things up
12639   if (strEqual(token, ARG_UNDEFINED))
12640     return ANIM_EVENT_ACTION_NONE;
12641
12642   int result = getImageIDFromToken(token);
12643
12644   if (result == -1)
12645   {
12646     char *gfx_token = getStringCat2("gfx.", token);
12647
12648     result = getImageIDFromToken(gfx_token);
12649
12650     checked_free(gfx_token);
12651   }
12652
12653   if (result == -1)
12654   {
12655     Key key = getKeyFromX11KeyName(token);
12656
12657     if (key != KSYM_UNDEFINED)
12658       result = -(int)key;
12659   }
12660
12661   if (result == -1)
12662   {
12663     if (isURL(token))
12664     {
12665       result = get_hash_from_string(token);     // unsigned int => int
12666       result = ABS(result);                     // may be negative now
12667       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12668
12669       setHashEntry(anim_url_hash, int2str(result, 0), token);
12670     }
12671   }
12672
12673   if (result == -1)
12674     result = ANIM_EVENT_ACTION_NONE;
12675
12676   return result;
12677 }
12678
12679 int get_parameter_value(char *value_raw, char *suffix, int type)
12680 {
12681   char *value = getStringToLower(value_raw);
12682   int result = 0;       // probably a save default value
12683
12684   if (strEqual(suffix, ".direction"))
12685   {
12686     result = (strEqual(value, "left")  ? MV_LEFT :
12687               strEqual(value, "right") ? MV_RIGHT :
12688               strEqual(value, "up")    ? MV_UP :
12689               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12690   }
12691   else if (strEqual(suffix, ".position"))
12692   {
12693     result = (strEqual(value, "left")   ? POS_LEFT :
12694               strEqual(value, "right")  ? POS_RIGHT :
12695               strEqual(value, "top")    ? POS_TOP :
12696               strEqual(value, "upper")  ? POS_UPPER :
12697               strEqual(value, "middle") ? POS_MIDDLE :
12698               strEqual(value, "lower")  ? POS_LOWER :
12699               strEqual(value, "bottom") ? POS_BOTTOM :
12700               strEqual(value, "any")    ? POS_ANY :
12701               strEqual(value, "ce")     ? POS_CE :
12702               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12703               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12704   }
12705   else if (strEqual(suffix, ".align"))
12706   {
12707     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12708               strEqual(value, "right")  ? ALIGN_RIGHT :
12709               strEqual(value, "center") ? ALIGN_CENTER :
12710               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12711   }
12712   else if (strEqual(suffix, ".valign"))
12713   {
12714     result = (strEqual(value, "top")    ? VALIGN_TOP :
12715               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12716               strEqual(value, "middle") ? VALIGN_MIDDLE :
12717               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12718   }
12719   else if (strEqual(suffix, ".anim_mode"))
12720   {
12721     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12722               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12723               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12724               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12725               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12726               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12727               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12728               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12729               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12730               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12731               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12732               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12733               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12734               string_has_parameter(value, "all")        ? ANIM_ALL :
12735               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12736               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12737               ANIM_DEFAULT);
12738
12739     if (string_has_parameter(value, "once"))
12740       result |= ANIM_ONCE;
12741
12742     if (string_has_parameter(value, "reverse"))
12743       result |= ANIM_REVERSE;
12744
12745     if (string_has_parameter(value, "opaque_player"))
12746       result |= ANIM_OPAQUE_PLAYER;
12747
12748     if (string_has_parameter(value, "static_panel"))
12749       result |= ANIM_STATIC_PANEL;
12750   }
12751   else if (strEqual(suffix, ".init_event") ||
12752            strEqual(suffix, ".anim_event"))
12753   {
12754     result = get_anim_parameter_values(value);
12755   }
12756   else if (strEqual(suffix, ".init_delay_action") ||
12757            strEqual(suffix, ".anim_delay_action") ||
12758            strEqual(suffix, ".post_delay_action") ||
12759            strEqual(suffix, ".init_event_action") ||
12760            strEqual(suffix, ".anim_event_action"))
12761   {
12762     result = get_anim_action_parameter_value(value_raw);
12763   }
12764   else if (strEqual(suffix, ".class"))
12765   {
12766     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12767               get_hash_from_string(value));
12768   }
12769   else if (strEqual(suffix, ".style"))
12770   {
12771     result = STYLE_DEFAULT;
12772
12773     if (string_has_parameter(value, "accurate_borders"))
12774       result |= STYLE_ACCURATE_BORDERS;
12775
12776     if (string_has_parameter(value, "inner_corners"))
12777       result |= STYLE_INNER_CORNERS;
12778
12779     if (string_has_parameter(value, "reverse"))
12780       result |= STYLE_REVERSE;
12781
12782     if (string_has_parameter(value, "leftmost_position"))
12783       result |= STYLE_LEFTMOST_POSITION;
12784
12785     if (string_has_parameter(value, "block_clicks"))
12786       result |= STYLE_BLOCK;
12787
12788     if (string_has_parameter(value, "passthrough_clicks"))
12789       result |= STYLE_PASSTHROUGH;
12790
12791     if (string_has_parameter(value, "multiple_actions"))
12792       result |= STYLE_MULTIPLE_ACTIONS;
12793
12794     if (string_has_parameter(value, "consume_ce_event"))
12795       result |= STYLE_CONSUME_CE_EVENT;
12796   }
12797   else if (strEqual(suffix, ".fade_mode"))
12798   {
12799     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12800               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12801               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12802               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12803               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12804               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12805               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12806               FADE_MODE_DEFAULT);
12807   }
12808   else if (strEqual(suffix, ".auto_delay_unit"))
12809   {
12810     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12811               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12812               AUTO_DELAY_UNIT_DEFAULT);
12813   }
12814   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12815   {
12816     result = gfx.get_font_from_token_function(value);
12817   }
12818   else          // generic parameter of type integer or boolean
12819   {
12820     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12821               type == TYPE_INTEGER ? get_integer_from_string(value) :
12822               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12823               ARG_UNDEFINED_VALUE);
12824   }
12825
12826   free(value);
12827
12828   return result;
12829 }
12830
12831 static int get_token_parameter_value(char *token, char *value_raw)
12832 {
12833   char *suffix;
12834
12835   if (token == NULL || value_raw == NULL)
12836     return ARG_UNDEFINED_VALUE;
12837
12838   suffix = strrchr(token, '.');
12839   if (suffix == NULL)
12840     suffix = token;
12841
12842   if (strEqual(suffix, ".element"))
12843     return getElementFromToken(value_raw);
12844
12845   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12846   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12847 }
12848
12849 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12850                                      boolean ignore_defaults)
12851 {
12852   int i;
12853
12854   for (i = 0; image_config_vars[i].token != NULL; i++)
12855   {
12856     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12857
12858     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12859     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12860       continue;
12861
12862     if (value != NULL)
12863       *image_config_vars[i].value =
12864         get_token_parameter_value(image_config_vars[i].token, value);
12865   }
12866 }
12867
12868 void InitMenuDesignSettings_Static(void)
12869 {
12870   // always start with reliable default values from static default config
12871   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12872 }
12873
12874 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12875 {
12876   int i;
12877
12878   // the following initializes hierarchical values from static configuration
12879
12880   // special case: initialize "ARG_DEFAULT" values in static default config
12881   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12882   titlescreen_initial_first_default.fade_mode  =
12883     title_initial_first_default.fade_mode;
12884   titlescreen_initial_first_default.fade_delay =
12885     title_initial_first_default.fade_delay;
12886   titlescreen_initial_first_default.post_delay =
12887     title_initial_first_default.post_delay;
12888   titlescreen_initial_first_default.auto_delay =
12889     title_initial_first_default.auto_delay;
12890   titlescreen_initial_first_default.auto_delay_unit =
12891     title_initial_first_default.auto_delay_unit;
12892   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12893   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12894   titlescreen_first_default.post_delay = title_first_default.post_delay;
12895   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12896   titlescreen_first_default.auto_delay_unit =
12897     title_first_default.auto_delay_unit;
12898   titlemessage_initial_first_default.fade_mode  =
12899     title_initial_first_default.fade_mode;
12900   titlemessage_initial_first_default.fade_delay =
12901     title_initial_first_default.fade_delay;
12902   titlemessage_initial_first_default.post_delay =
12903     title_initial_first_default.post_delay;
12904   titlemessage_initial_first_default.auto_delay =
12905     title_initial_first_default.auto_delay;
12906   titlemessage_initial_first_default.auto_delay_unit =
12907     title_initial_first_default.auto_delay_unit;
12908   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12909   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12910   titlemessage_first_default.post_delay = title_first_default.post_delay;
12911   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12912   titlemessage_first_default.auto_delay_unit =
12913     title_first_default.auto_delay_unit;
12914
12915   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12916   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12917   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12918   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12919   titlescreen_initial_default.auto_delay_unit =
12920     title_initial_default.auto_delay_unit;
12921   titlescreen_default.fade_mode  = title_default.fade_mode;
12922   titlescreen_default.fade_delay = title_default.fade_delay;
12923   titlescreen_default.post_delay = title_default.post_delay;
12924   titlescreen_default.auto_delay = title_default.auto_delay;
12925   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12926   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12927   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12928   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12929   titlemessage_initial_default.auto_delay_unit =
12930     title_initial_default.auto_delay_unit;
12931   titlemessage_default.fade_mode  = title_default.fade_mode;
12932   titlemessage_default.fade_delay = title_default.fade_delay;
12933   titlemessage_default.post_delay = title_default.post_delay;
12934   titlemessage_default.auto_delay = title_default.auto_delay;
12935   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12936
12937   // special case: initialize "ARG_DEFAULT" values in static default config
12938   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12939   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12940   {
12941     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12942     titlescreen_first[i] = titlescreen_first_default;
12943     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12944     titlemessage_first[i] = titlemessage_first_default;
12945
12946     titlescreen_initial[i] = titlescreen_initial_default;
12947     titlescreen[i] = titlescreen_default;
12948     titlemessage_initial[i] = titlemessage_initial_default;
12949     titlemessage[i] = titlemessage_default;
12950   }
12951
12952   // special case: initialize "ARG_DEFAULT" values in static default config
12953   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12954   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12955   {
12956     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12957       continue;
12958
12959     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12960     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12961     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12962   }
12963
12964   // special case: initialize "ARG_DEFAULT" values in static default config
12965   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12966   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12967   {
12968     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12969     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12970     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12971
12972     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12973       continue;
12974
12975     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12976   }
12977 }
12978
12979 static void InitMenuDesignSettings_SpecialPostProcessing(void)
12980 {
12981   static struct
12982   {
12983     struct XY *dst, *src;
12984   }
12985   game_buttons_xy[] =
12986   {
12987     { &game.button.save,        &game.button.stop       },
12988     { &game.button.pause2,      &game.button.pause      },
12989     { &game.button.load,        &game.button.play       },
12990     { &game.button.undo,        &game.button.stop       },
12991     { &game.button.redo,        &game.button.play       },
12992
12993     { NULL,                     NULL                    }
12994   };
12995   int i, j;
12996
12997   // special case: initialize later added SETUP list size from LEVELS value
12998   if (menu.list_size[GAME_MODE_SETUP] == -1)
12999     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13000
13001   // set default position for snapshot buttons to stop/pause/play buttons
13002   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13003     if ((*game_buttons_xy[i].dst).x == -1 &&
13004         (*game_buttons_xy[i].dst).y == -1)
13005       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13006
13007   // --------------------------------------------------------------------------
13008   // dynamic viewports (including playfield margins, borders and alignments)
13009   // --------------------------------------------------------------------------
13010
13011   // dynamic viewports currently only supported for landscape mode
13012   int display_width  = MAX(video.display_width, video.display_height);
13013   int display_height = MIN(video.display_width, video.display_height);
13014
13015   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13016   {
13017     struct RectWithBorder *vp_window    = &viewport.window[i];
13018     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13019     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13020     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13021     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13022     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13023     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13024     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13025
13026     // adjust window size if min/max width/height is specified
13027
13028     if (vp_window->min_width != -1)
13029     {
13030       int window_width = display_width;
13031
13032       // when using static window height, use aspect ratio of display
13033       if (vp_window->min_height == -1)
13034         window_width = vp_window->height * display_width / display_height;
13035
13036       vp_window->width = MAX(vp_window->min_width, window_width);
13037     }
13038
13039     if (vp_window->min_height != -1)
13040     {
13041       int window_height = display_height;
13042
13043       // when using static window width, use aspect ratio of display
13044       if (vp_window->min_width == -1)
13045         window_height = vp_window->width * display_height / display_width;
13046
13047       vp_window->height = MAX(vp_window->min_height, window_height);
13048     }
13049
13050     if (vp_window->max_width != -1)
13051       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13052
13053     if (vp_window->max_height != -1)
13054       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13055
13056     int playfield_width  = vp_window->width;
13057     int playfield_height = vp_window->height;
13058
13059     // adjust playfield size and position according to specified margins
13060
13061     playfield_width  -= vp_playfield->margin_left;
13062     playfield_width  -= vp_playfield->margin_right;
13063
13064     playfield_height -= vp_playfield->margin_top;
13065     playfield_height -= vp_playfield->margin_bottom;
13066
13067     // adjust playfield size if min/max width/height is specified
13068
13069     if (vp_playfield->min_width != -1)
13070       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13071
13072     if (vp_playfield->min_height != -1)
13073       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13074
13075     if (vp_playfield->max_width != -1)
13076       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13077
13078     if (vp_playfield->max_height != -1)
13079       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13080
13081     // adjust playfield position according to specified alignment
13082
13083     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13084       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13085     else if (vp_playfield->align == ALIGN_CENTER)
13086       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13087     else if (vp_playfield->align == ALIGN_RIGHT)
13088       vp_playfield->x += playfield_width - vp_playfield->width;
13089
13090     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13091       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13092     else if (vp_playfield->valign == VALIGN_MIDDLE)
13093       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13094     else if (vp_playfield->valign == VALIGN_BOTTOM)
13095       vp_playfield->y += playfield_height - vp_playfield->height;
13096
13097     vp_playfield->x += vp_playfield->margin_left;
13098     vp_playfield->y += vp_playfield->margin_top;
13099
13100     // adjust individual playfield borders if only default border is specified
13101
13102     if (vp_playfield->border_left == -1)
13103       vp_playfield->border_left = vp_playfield->border_size;
13104     if (vp_playfield->border_right == -1)
13105       vp_playfield->border_right = vp_playfield->border_size;
13106     if (vp_playfield->border_top == -1)
13107       vp_playfield->border_top = vp_playfield->border_size;
13108     if (vp_playfield->border_bottom == -1)
13109       vp_playfield->border_bottom = vp_playfield->border_size;
13110
13111     // set dynamic playfield borders if borders are specified as undefined
13112     // (but only if window size was dynamic and playfield size was static)
13113
13114     if (dynamic_window_width && !dynamic_playfield_width)
13115     {
13116       if (vp_playfield->border_left == -1)
13117       {
13118         vp_playfield->border_left = (vp_playfield->x -
13119                                      vp_playfield->margin_left);
13120         vp_playfield->x     -= vp_playfield->border_left;
13121         vp_playfield->width += vp_playfield->border_left;
13122       }
13123
13124       if (vp_playfield->border_right == -1)
13125       {
13126         vp_playfield->border_right = (vp_window->width -
13127                                       vp_playfield->x -
13128                                       vp_playfield->width -
13129                                       vp_playfield->margin_right);
13130         vp_playfield->width += vp_playfield->border_right;
13131       }
13132     }
13133
13134     if (dynamic_window_height && !dynamic_playfield_height)
13135     {
13136       if (vp_playfield->border_top == -1)
13137       {
13138         vp_playfield->border_top = (vp_playfield->y -
13139                                     vp_playfield->margin_top);
13140         vp_playfield->y      -= vp_playfield->border_top;
13141         vp_playfield->height += vp_playfield->border_top;
13142       }
13143
13144       if (vp_playfield->border_bottom == -1)
13145       {
13146         vp_playfield->border_bottom = (vp_window->height -
13147                                        vp_playfield->y -
13148                                        vp_playfield->height -
13149                                        vp_playfield->margin_bottom);
13150         vp_playfield->height += vp_playfield->border_bottom;
13151       }
13152     }
13153
13154     // adjust playfield size to be a multiple of a defined alignment tile size
13155
13156     int align_size = vp_playfield->align_size;
13157     int playfield_xtiles = vp_playfield->width  / align_size;
13158     int playfield_ytiles = vp_playfield->height / align_size;
13159     int playfield_width_corrected  = playfield_xtiles * align_size;
13160     int playfield_height_corrected = playfield_ytiles * align_size;
13161     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13162                                  i == GFX_SPECIAL_ARG_EDITOR);
13163
13164     if (is_playfield_mode &&
13165         dynamic_playfield_width &&
13166         vp_playfield->width != playfield_width_corrected)
13167     {
13168       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13169
13170       vp_playfield->width = playfield_width_corrected;
13171
13172       if (vp_playfield->align == ALIGN_LEFT)
13173       {
13174         vp_playfield->border_left += playfield_xdiff;
13175       }
13176       else if (vp_playfield->align == ALIGN_RIGHT)
13177       {
13178         vp_playfield->border_right += playfield_xdiff;
13179       }
13180       else if (vp_playfield->align == ALIGN_CENTER)
13181       {
13182         int border_left_diff  = playfield_xdiff / 2;
13183         int border_right_diff = playfield_xdiff - border_left_diff;
13184
13185         vp_playfield->border_left  += border_left_diff;
13186         vp_playfield->border_right += border_right_diff;
13187       }
13188     }
13189
13190     if (is_playfield_mode &&
13191         dynamic_playfield_height &&
13192         vp_playfield->height != playfield_height_corrected)
13193     {
13194       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13195
13196       vp_playfield->height = playfield_height_corrected;
13197
13198       if (vp_playfield->valign == VALIGN_TOP)
13199       {
13200         vp_playfield->border_top += playfield_ydiff;
13201       }
13202       else if (vp_playfield->align == VALIGN_BOTTOM)
13203       {
13204         vp_playfield->border_right += playfield_ydiff;
13205       }
13206       else if (vp_playfield->align == VALIGN_MIDDLE)
13207       {
13208         int border_top_diff    = playfield_ydiff / 2;
13209         int border_bottom_diff = playfield_ydiff - border_top_diff;
13210
13211         vp_playfield->border_top    += border_top_diff;
13212         vp_playfield->border_bottom += border_bottom_diff;
13213       }
13214     }
13215
13216     // adjust door positions according to specified alignment
13217
13218     for (j = 0; j < 2; j++)
13219     {
13220       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13221
13222       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13223         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13224       else if (vp_door->align == ALIGN_CENTER)
13225         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13226       else if (vp_door->align == ALIGN_RIGHT)
13227         vp_door->x += vp_window->width - vp_door->width;
13228
13229       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13230         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13231       else if (vp_door->valign == VALIGN_MIDDLE)
13232         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13233       else if (vp_door->valign == VALIGN_BOTTOM)
13234         vp_door->y += vp_window->height - vp_door->height;
13235     }
13236   }
13237 }
13238
13239 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13240 {
13241   static struct
13242   {
13243     struct XYTileSize *dst, *src;
13244     int graphic;
13245   }
13246   editor_buttons_xy[] =
13247   {
13248     {
13249       &editor.button.element_left,      &editor.palette.element_left,
13250       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13251     },
13252     {
13253       &editor.button.element_middle,    &editor.palette.element_middle,
13254       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13255     },
13256     {
13257       &editor.button.element_right,     &editor.palette.element_right,
13258       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13259     },
13260
13261     { NULL,                     NULL                    }
13262   };
13263   int i;
13264
13265   // set default position for element buttons to element graphics
13266   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13267   {
13268     if ((*editor_buttons_xy[i].dst).x == -1 &&
13269         (*editor_buttons_xy[i].dst).y == -1)
13270     {
13271       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13272
13273       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13274
13275       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13276     }
13277   }
13278
13279   // adjust editor palette rows and columns if specified to be dynamic
13280
13281   if (editor.palette.cols == -1)
13282   {
13283     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13284     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13285     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13286
13287     editor.palette.cols = (vp_width - sc_width) / bt_width;
13288
13289     if (editor.palette.x == -1)
13290     {
13291       int palette_width = editor.palette.cols * bt_width + sc_width;
13292
13293       editor.palette.x = (vp_width - palette_width) / 2;
13294     }
13295   }
13296
13297   if (editor.palette.rows == -1)
13298   {
13299     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13300     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13301     int tx_height = getFontHeight(FONT_TEXT_2);
13302
13303     editor.palette.rows = (vp_height - tx_height) / bt_height;
13304
13305     if (editor.palette.y == -1)
13306     {
13307       int palette_height = editor.palette.rows * bt_height + tx_height;
13308
13309       editor.palette.y = (vp_height - palette_height) / 2;
13310     }
13311   }
13312 }
13313
13314 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13315                                                       boolean initialize)
13316 {
13317   // special case: check if network and preview player positions are redefined,
13318   // to compare this later against the main menu level preview being redefined
13319   struct TokenIntPtrInfo menu_config_players[] =
13320   {
13321     { "main.network_players.x", &menu.main.network_players.redefined    },
13322     { "main.network_players.y", &menu.main.network_players.redefined    },
13323     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13324     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13325     { "preview.x",              &preview.redefined                      },
13326     { "preview.y",              &preview.redefined                      }
13327   };
13328   int i;
13329
13330   if (initialize)
13331   {
13332     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13333       *menu_config_players[i].value = FALSE;
13334   }
13335   else
13336   {
13337     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13338       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13339         *menu_config_players[i].value = TRUE;
13340   }
13341 }
13342
13343 static void InitMenuDesignSettings_PreviewPlayers(void)
13344 {
13345   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13346 }
13347
13348 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13349 {
13350   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13351 }
13352
13353 static void LoadMenuDesignSettingsFromFilename(char *filename)
13354 {
13355   static struct TitleFadingInfo tfi;
13356   static struct TitleMessageInfo tmi;
13357   static struct TokenInfo title_tokens[] =
13358   {
13359     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13360     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13361     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13362     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13363     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13364
13365     { -1,               NULL,                   NULL                    }
13366   };
13367   static struct TokenInfo titlemessage_tokens[] =
13368   {
13369     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13370     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13371     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13372     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13373     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13374     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13375     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13376     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13377     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13378     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13379     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13380     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13381     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13382     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13383     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13384     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13385     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13386     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13387
13388     { -1,               NULL,                   NULL                    }
13389   };
13390   static struct
13391   {
13392     struct TitleFadingInfo *info;
13393     char *text;
13394   }
13395   title_info[] =
13396   {
13397     // initialize first titles from "enter screen" definitions, if defined
13398     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13399     { &title_first_default,             "menu.enter_screen.TITLE"       },
13400
13401     // initialize title screens from "next screen" definitions, if defined
13402     { &title_initial_default,           "menu.next_screen.TITLE"        },
13403     { &title_default,                   "menu.next_screen.TITLE"        },
13404
13405     { NULL,                             NULL                            }
13406   };
13407   static struct
13408   {
13409     struct TitleMessageInfo *array;
13410     char *text;
13411   }
13412   titlemessage_arrays[] =
13413   {
13414     // initialize first titles from "enter screen" definitions, if defined
13415     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13416     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13417     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13418     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13419
13420     // initialize titles from "next screen" definitions, if defined
13421     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13422     { titlescreen,                      "menu.next_screen.TITLE"        },
13423     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13424     { titlemessage,                     "menu.next_screen.TITLE"        },
13425
13426     // overwrite titles with title definitions, if defined
13427     { titlescreen_initial_first,        "[title_initial]"               },
13428     { titlescreen_first,                "[title]"                       },
13429     { titlemessage_initial_first,       "[title_initial]"               },
13430     { titlemessage_first,               "[title]"                       },
13431
13432     { titlescreen_initial,              "[title_initial]"               },
13433     { titlescreen,                      "[title]"                       },
13434     { titlemessage_initial,             "[title_initial]"               },
13435     { titlemessage,                     "[title]"                       },
13436
13437     // overwrite titles with title screen/message definitions, if defined
13438     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13439     { titlescreen_first,                "[titlescreen]"                 },
13440     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13441     { titlemessage_first,               "[titlemessage]"                },
13442
13443     { titlescreen_initial,              "[titlescreen_initial]"         },
13444     { titlescreen,                      "[titlescreen]"                 },
13445     { titlemessage_initial,             "[titlemessage_initial]"        },
13446     { titlemessage,                     "[titlemessage]"                },
13447
13448     { NULL,                             NULL                            }
13449   };
13450   SetupFileHash *setup_file_hash;
13451   int i, j, k;
13452
13453   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13454     return;
13455
13456   // the following initializes hierarchical values from dynamic configuration
13457
13458   // special case: initialize with default values that may be overwritten
13459   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13460   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13461   {
13462     struct TokenIntPtrInfo menu_config[] =
13463     {
13464       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13465       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13466       { "menu.list_size",       &menu.list_size[i]      }
13467     };
13468
13469     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13470     {
13471       char *token = menu_config[j].token;
13472       char *value = getHashEntry(setup_file_hash, token);
13473
13474       if (value != NULL)
13475         *menu_config[j].value = get_integer_from_string(value);
13476     }
13477   }
13478
13479   // special case: initialize with default values that may be overwritten
13480   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13481   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13482   {
13483     struct TokenIntPtrInfo menu_config[] =
13484     {
13485       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13486       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13487       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13488       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13489       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13490     };
13491
13492     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13493     {
13494       char *token = menu_config[j].token;
13495       char *value = getHashEntry(setup_file_hash, token);
13496
13497       if (value != NULL)
13498         *menu_config[j].value = get_integer_from_string(value);
13499     }
13500   }
13501
13502   // special case: initialize with default values that may be overwritten
13503   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13504   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13505   {
13506     struct TokenIntPtrInfo menu_config[] =
13507     {
13508       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13509       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13510     };
13511
13512     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13513     {
13514       char *token = menu_config[j].token;
13515       char *value = getHashEntry(setup_file_hash, token);
13516
13517       if (value != NULL)
13518         *menu_config[j].value = get_integer_from_string(value);
13519     }
13520   }
13521
13522   // special case: initialize with default values that may be overwritten
13523   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13524   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13525   {
13526     struct TokenIntPtrInfo menu_config[] =
13527     {
13528       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13529       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13530       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13531       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13532       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13533       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13534       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13535       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13536       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13537       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13538     };
13539
13540     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13541     {
13542       char *token = menu_config[j].token;
13543       char *value = getHashEntry(setup_file_hash, token);
13544
13545       if (value != NULL)
13546         *menu_config[j].value = get_integer_from_string(value);
13547     }
13548   }
13549
13550   // special case: initialize with default values that may be overwritten
13551   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13552   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13553   {
13554     struct TokenIntPtrInfo menu_config[] =
13555     {
13556       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13557       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13558       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13559       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13560       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13561       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13562       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13563       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13564       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13565     };
13566
13567     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13568     {
13569       char *token = menu_config[j].token;
13570       char *value = getHashEntry(setup_file_hash, token);
13571
13572       if (value != NULL)
13573         *menu_config[j].value = get_token_parameter_value(token, value);
13574     }
13575   }
13576
13577   // special case: initialize with default values that may be overwritten
13578   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13579   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13580   {
13581     struct
13582     {
13583       char *token_prefix;
13584       struct RectWithBorder *struct_ptr;
13585     }
13586     vp_struct[] =
13587     {
13588       { "viewport.window",      &viewport.window[i]     },
13589       { "viewport.playfield",   &viewport.playfield[i]  },
13590       { "viewport.door_1",      &viewport.door_1[i]     },
13591       { "viewport.door_2",      &viewport.door_2[i]     }
13592     };
13593
13594     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13595     {
13596       struct TokenIntPtrInfo vp_config[] =
13597       {
13598         { ".x",                 &vp_struct[j].struct_ptr->x             },
13599         { ".y",                 &vp_struct[j].struct_ptr->y             },
13600         { ".width",             &vp_struct[j].struct_ptr->width         },
13601         { ".height",            &vp_struct[j].struct_ptr->height        },
13602         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13603         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13604         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13605         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13606         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13607         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13608         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13609         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13610         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13611         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13612         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13613         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13614         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13615         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13616         { ".align",             &vp_struct[j].struct_ptr->align         },
13617         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13618       };
13619
13620       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13621       {
13622         char *token = getStringCat2(vp_struct[j].token_prefix,
13623                                     vp_config[k].token);
13624         char *value = getHashEntry(setup_file_hash, token);
13625
13626         if (value != NULL)
13627           *vp_config[k].value = get_token_parameter_value(token, value);
13628
13629         free(token);
13630       }
13631     }
13632   }
13633
13634   // special case: initialize with default values that may be overwritten
13635   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13636   for (i = 0; title_info[i].info != NULL; i++)
13637   {
13638     struct TitleFadingInfo *info = title_info[i].info;
13639     char *base_token = title_info[i].text;
13640
13641     for (j = 0; title_tokens[j].type != -1; j++)
13642     {
13643       char *token = getStringCat2(base_token, title_tokens[j].text);
13644       char *value = getHashEntry(setup_file_hash, token);
13645
13646       if (value != NULL)
13647       {
13648         int parameter_value = get_token_parameter_value(token, value);
13649
13650         tfi = *info;
13651
13652         *(int *)title_tokens[j].value = (int)parameter_value;
13653
13654         *info = tfi;
13655       }
13656
13657       free(token);
13658     }
13659   }
13660
13661   // special case: initialize with default values that may be overwritten
13662   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13663   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13664   {
13665     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13666     char *base_token = titlemessage_arrays[i].text;
13667
13668     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13669     {
13670       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13671       char *value = getHashEntry(setup_file_hash, token);
13672
13673       if (value != NULL)
13674       {
13675         int parameter_value = get_token_parameter_value(token, value);
13676
13677         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13678         {
13679           tmi = array[k];
13680
13681           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13682             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13683           else
13684             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13685
13686           array[k] = tmi;
13687         }
13688       }
13689
13690       free(token);
13691     }
13692   }
13693
13694   // read (and overwrite with) values that may be specified in config file
13695   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13696
13697   // special case: check if network and preview player positions are redefined
13698   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13699
13700   freeSetupFileHash(setup_file_hash);
13701 }
13702
13703 void LoadMenuDesignSettings(void)
13704 {
13705   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13706
13707   InitMenuDesignSettings_Static();
13708   InitMenuDesignSettings_SpecialPreProcessing();
13709   InitMenuDesignSettings_PreviewPlayers();
13710
13711   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13712   {
13713     // first look for special settings configured in level series config
13714     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13715
13716     if (fileExists(filename_base))
13717       LoadMenuDesignSettingsFromFilename(filename_base);
13718   }
13719
13720   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13721
13722   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13723     LoadMenuDesignSettingsFromFilename(filename_local);
13724
13725   InitMenuDesignSettings_SpecialPostProcessing();
13726 }
13727
13728 void LoadMenuDesignSettings_AfterGraphics(void)
13729 {
13730   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13731 }
13732
13733 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13734                                 boolean ignore_defaults)
13735 {
13736   int i;
13737
13738   for (i = 0; sound_config_vars[i].token != NULL; i++)
13739   {
13740     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13741
13742     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13743     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13744       continue;
13745
13746     if (value != NULL)
13747       *sound_config_vars[i].value =
13748         get_token_parameter_value(sound_config_vars[i].token, value);
13749   }
13750 }
13751
13752 void InitSoundSettings_Static(void)
13753 {
13754   // always start with reliable default values from static default config
13755   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13756 }
13757
13758 static void LoadSoundSettingsFromFilename(char *filename)
13759 {
13760   SetupFileHash *setup_file_hash;
13761
13762   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13763     return;
13764
13765   // read (and overwrite with) values that may be specified in config file
13766   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13767
13768   freeSetupFileHash(setup_file_hash);
13769 }
13770
13771 void LoadSoundSettings(void)
13772 {
13773   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13774
13775   InitSoundSettings_Static();
13776
13777   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13778   {
13779     // first look for special settings configured in level series config
13780     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13781
13782     if (fileExists(filename_base))
13783       LoadSoundSettingsFromFilename(filename_base);
13784   }
13785
13786   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13787
13788   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13789     LoadSoundSettingsFromFilename(filename_local);
13790 }
13791
13792 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13793 {
13794   char *filename = getEditorSetupFilename();
13795   SetupFileList *setup_file_list, *list;
13796   SetupFileHash *element_hash;
13797   int num_unknown_tokens = 0;
13798   int i;
13799
13800   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13801     return;
13802
13803   element_hash = newSetupFileHash();
13804
13805   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13806     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13807
13808   // determined size may be larger than needed (due to unknown elements)
13809   *num_elements = 0;
13810   for (list = setup_file_list; list != NULL; list = list->next)
13811     (*num_elements)++;
13812
13813   // add space for up to 3 more elements for padding that may be needed
13814   *num_elements += 3;
13815
13816   // free memory for old list of elements, if needed
13817   checked_free(*elements);
13818
13819   // allocate memory for new list of elements
13820   *elements = checked_malloc(*num_elements * sizeof(int));
13821
13822   *num_elements = 0;
13823   for (list = setup_file_list; list != NULL; list = list->next)
13824   {
13825     char *value = getHashEntry(element_hash, list->token);
13826
13827     if (value == NULL)          // try to find obsolete token mapping
13828     {
13829       char *mapped_token = get_mapped_token(list->token);
13830
13831       if (mapped_token != NULL)
13832       {
13833         value = getHashEntry(element_hash, mapped_token);
13834
13835         free(mapped_token);
13836       }
13837     }
13838
13839     if (value != NULL)
13840     {
13841       (*elements)[(*num_elements)++] = atoi(value);
13842     }
13843     else
13844     {
13845       if (num_unknown_tokens == 0)
13846       {
13847         Warn("---");
13848         Warn("unknown token(s) found in config file:");
13849         Warn("- config file: '%s'", filename);
13850
13851         num_unknown_tokens++;
13852       }
13853
13854       Warn("- token: '%s'", list->token);
13855     }
13856   }
13857
13858   if (num_unknown_tokens > 0)
13859     Warn("---");
13860
13861   while (*num_elements % 4)     // pad with empty elements, if needed
13862     (*elements)[(*num_elements)++] = EL_EMPTY;
13863
13864   freeSetupFileList(setup_file_list);
13865   freeSetupFileHash(element_hash);
13866
13867 #if 0
13868   for (i = 0; i < *num_elements; i++)
13869     Debug("editor", "element '%s' [%d]\n",
13870           element_info[(*elements)[i]].token_name, (*elements)[i]);
13871 #endif
13872 }
13873
13874 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13875                                                      boolean is_sound)
13876 {
13877   SetupFileHash *setup_file_hash = NULL;
13878   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13879   char *filename_music, *filename_prefix, *filename_info;
13880   struct
13881   {
13882     char *token;
13883     char **value_ptr;
13884   }
13885   token_to_value_ptr[] =
13886   {
13887     { "title_header",   &tmp_music_file_info.title_header       },
13888     { "artist_header",  &tmp_music_file_info.artist_header      },
13889     { "album_header",   &tmp_music_file_info.album_header       },
13890     { "year_header",    &tmp_music_file_info.year_header        },
13891     { "played_header",  &tmp_music_file_info.played_header      },
13892
13893     { "title",          &tmp_music_file_info.title              },
13894     { "artist",         &tmp_music_file_info.artist             },
13895     { "album",          &tmp_music_file_info.album              },
13896     { "year",           &tmp_music_file_info.year               },
13897     { "played",         &tmp_music_file_info.played             },
13898
13899     { NULL,             NULL                                    },
13900   };
13901   int i;
13902
13903   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13904                     getCustomMusicFilename(basename));
13905
13906   if (filename_music == NULL)
13907     return NULL;
13908
13909   // ---------- try to replace file extension ----------
13910
13911   filename_prefix = getStringCopy(filename_music);
13912   if (strrchr(filename_prefix, '.') != NULL)
13913     *strrchr(filename_prefix, '.') = '\0';
13914   filename_info = getStringCat2(filename_prefix, ".txt");
13915
13916   if (fileExists(filename_info))
13917     setup_file_hash = loadSetupFileHash(filename_info);
13918
13919   free(filename_prefix);
13920   free(filename_info);
13921
13922   if (setup_file_hash == NULL)
13923   {
13924     // ---------- try to add file extension ----------
13925
13926     filename_prefix = getStringCopy(filename_music);
13927     filename_info = getStringCat2(filename_prefix, ".txt");
13928
13929     if (fileExists(filename_info))
13930       setup_file_hash = loadSetupFileHash(filename_info);
13931
13932     free(filename_prefix);
13933     free(filename_info);
13934   }
13935
13936   if (setup_file_hash == NULL)
13937     return NULL;
13938
13939   // ---------- music file info found ----------
13940
13941   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13942
13943   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13944   {
13945     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13946
13947     *token_to_value_ptr[i].value_ptr =
13948       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13949   }
13950
13951   tmp_music_file_info.basename = getStringCopy(basename);
13952   tmp_music_file_info.music = music;
13953   tmp_music_file_info.is_sound = is_sound;
13954
13955   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13956   *new_music_file_info = tmp_music_file_info;
13957
13958   return new_music_file_info;
13959 }
13960
13961 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13962 {
13963   return get_music_file_info_ext(basename, music, FALSE);
13964 }
13965
13966 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13967 {
13968   return get_music_file_info_ext(basename, sound, TRUE);
13969 }
13970
13971 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13972                                      char *basename, boolean is_sound)
13973 {
13974   for (; list != NULL; list = list->next)
13975     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13976       return TRUE;
13977
13978   return FALSE;
13979 }
13980
13981 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
13982 {
13983   return music_info_listed_ext(list, basename, FALSE);
13984 }
13985
13986 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
13987 {
13988   return music_info_listed_ext(list, basename, TRUE);
13989 }
13990
13991 void LoadMusicInfo(void)
13992 {
13993   int num_music_noconf = getMusicListSize_NoConf();
13994   int num_music = getMusicListSize();
13995   int num_sounds = getSoundListSize();
13996   struct FileInfo *music, *sound;
13997   struct MusicFileInfo *next, **new;
13998
13999   int i;
14000
14001   while (music_file_info != NULL)
14002   {
14003     next = music_file_info->next;
14004
14005     checked_free(music_file_info->basename);
14006
14007     checked_free(music_file_info->title_header);
14008     checked_free(music_file_info->artist_header);
14009     checked_free(music_file_info->album_header);
14010     checked_free(music_file_info->year_header);
14011     checked_free(music_file_info->played_header);
14012
14013     checked_free(music_file_info->title);
14014     checked_free(music_file_info->artist);
14015     checked_free(music_file_info->album);
14016     checked_free(music_file_info->year);
14017     checked_free(music_file_info->played);
14018
14019     free(music_file_info);
14020
14021     music_file_info = next;
14022   }
14023
14024   new = &music_file_info;
14025
14026   // get (configured or unconfigured) music file info for all levels
14027   for (i = leveldir_current->first_level;
14028        i <= leveldir_current->last_level; i++)
14029   {
14030     int music_nr;
14031
14032     if (levelset.music[i] != MUS_UNDEFINED)
14033     {
14034       // get music file info for configured level music
14035       music_nr = levelset.music[i];
14036     }
14037     else if (num_music_noconf > 0)
14038     {
14039       // get music file info for unconfigured level music
14040       int level_pos = i - leveldir_current->first_level;
14041
14042       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14043     }
14044     else
14045     {
14046       continue;
14047     }
14048
14049     char *basename = getMusicInfoEntryFilename(music_nr);
14050
14051     if (basename == NULL)
14052       continue;
14053
14054     if (!music_info_listed(music_file_info, basename))
14055     {
14056       *new = get_music_file_info(basename, music_nr);
14057
14058       if (*new != NULL)
14059         new = &(*new)->next;
14060     }
14061   }
14062
14063   // get music file info for all remaining configured music files
14064   for (i = 0; i < num_music; i++)
14065   {
14066     music = getMusicListEntry(i);
14067
14068     if (music->filename == NULL)
14069       continue;
14070
14071     if (strEqual(music->filename, UNDEFINED_FILENAME))
14072       continue;
14073
14074     // a configured file may be not recognized as music
14075     if (!FileIsMusic(music->filename))
14076       continue;
14077
14078     if (!music_info_listed(music_file_info, music->filename))
14079     {
14080       *new = get_music_file_info(music->filename, i);
14081
14082       if (*new != NULL)
14083         new = &(*new)->next;
14084     }
14085   }
14086
14087   // get sound file info for all configured sound files
14088   for (i = 0; i < num_sounds; i++)
14089   {
14090     sound = getSoundListEntry(i);
14091
14092     if (sound->filename == NULL)
14093       continue;
14094
14095     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14096       continue;
14097
14098     // a configured file may be not recognized as sound
14099     if (!FileIsSound(sound->filename))
14100       continue;
14101
14102     if (!sound_info_listed(music_file_info, sound->filename))
14103     {
14104       *new = get_sound_file_info(sound->filename, i);
14105       if (*new != NULL)
14106         new = &(*new)->next;
14107     }
14108   }
14109
14110   // add pointers to previous list nodes
14111
14112   struct MusicFileInfo *node = music_file_info;
14113
14114   while (node != NULL)
14115   {
14116     if (node->next)
14117       node->next->prev = node;
14118
14119     node = node->next;
14120   }
14121 }
14122
14123 static void add_helpanim_entry(int element, int action, int direction,
14124                                int delay, int *num_list_entries)
14125 {
14126   struct HelpAnimInfo *new_list_entry;
14127   (*num_list_entries)++;
14128
14129   helpanim_info =
14130     checked_realloc(helpanim_info,
14131                     *num_list_entries * sizeof(struct HelpAnimInfo));
14132   new_list_entry = &helpanim_info[*num_list_entries - 1];
14133
14134   new_list_entry->element = element;
14135   new_list_entry->action = action;
14136   new_list_entry->direction = direction;
14137   new_list_entry->delay = delay;
14138 }
14139
14140 static void print_unknown_token(char *filename, char *token, int token_nr)
14141 {
14142   if (token_nr == 0)
14143   {
14144     Warn("---");
14145     Warn("unknown token(s) found in config file:");
14146     Warn("- config file: '%s'", filename);
14147   }
14148
14149   Warn("- token: '%s'", token);
14150 }
14151
14152 static void print_unknown_token_end(int token_nr)
14153 {
14154   if (token_nr > 0)
14155     Warn("---");
14156 }
14157
14158 void LoadHelpAnimInfo(void)
14159 {
14160   char *filename = getHelpAnimFilename();
14161   SetupFileList *setup_file_list = NULL, *list;
14162   SetupFileHash *element_hash, *action_hash, *direction_hash;
14163   int num_list_entries = 0;
14164   int num_unknown_tokens = 0;
14165   int i;
14166
14167   if (fileExists(filename))
14168     setup_file_list = loadSetupFileList(filename);
14169
14170   if (setup_file_list == NULL)
14171   {
14172     // use reliable default values from static configuration
14173     SetupFileList *insert_ptr;
14174
14175     insert_ptr = setup_file_list =
14176       newSetupFileList(helpanim_config[0].token,
14177                        helpanim_config[0].value);
14178
14179     for (i = 1; helpanim_config[i].token; i++)
14180       insert_ptr = addListEntry(insert_ptr,
14181                                 helpanim_config[i].token,
14182                                 helpanim_config[i].value);
14183   }
14184
14185   element_hash   = newSetupFileHash();
14186   action_hash    = newSetupFileHash();
14187   direction_hash = newSetupFileHash();
14188
14189   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14190     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14191
14192   for (i = 0; i < NUM_ACTIONS; i++)
14193     setHashEntry(action_hash, element_action_info[i].suffix,
14194                  i_to_a(element_action_info[i].value));
14195
14196   // do not store direction index (bit) here, but direction value!
14197   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14198     setHashEntry(direction_hash, element_direction_info[i].suffix,
14199                  i_to_a(1 << element_direction_info[i].value));
14200
14201   for (list = setup_file_list; list != NULL; list = list->next)
14202   {
14203     char *element_token, *action_token, *direction_token;
14204     char *element_value, *action_value, *direction_value;
14205     int delay = atoi(list->value);
14206
14207     if (strEqual(list->token, "end"))
14208     {
14209       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14210
14211       continue;
14212     }
14213
14214     /* first try to break element into element/action/direction parts;
14215        if this does not work, also accept combined "element[.act][.dir]"
14216        elements (like "dynamite.active"), which are unique elements */
14217
14218     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14219     {
14220       element_value = getHashEntry(element_hash, list->token);
14221       if (element_value != NULL)        // element found
14222         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14223                            &num_list_entries);
14224       else
14225       {
14226         // no further suffixes found -- this is not an element
14227         print_unknown_token(filename, list->token, num_unknown_tokens++);
14228       }
14229
14230       continue;
14231     }
14232
14233     // token has format "<prefix>.<something>"
14234
14235     action_token = strchr(list->token, '.');    // suffix may be action ...
14236     direction_token = action_token;             // ... or direction
14237
14238     element_token = getStringCopy(list->token);
14239     *strchr(element_token, '.') = '\0';
14240
14241     element_value = getHashEntry(element_hash, element_token);
14242
14243     if (element_value == NULL)          // this is no element
14244     {
14245       element_value = getHashEntry(element_hash, list->token);
14246       if (element_value != NULL)        // combined element found
14247         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14248                            &num_list_entries);
14249       else
14250         print_unknown_token(filename, list->token, num_unknown_tokens++);
14251
14252       free(element_token);
14253
14254       continue;
14255     }
14256
14257     action_value = getHashEntry(action_hash, action_token);
14258
14259     if (action_value != NULL)           // action found
14260     {
14261       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14262                     &num_list_entries);
14263
14264       free(element_token);
14265
14266       continue;
14267     }
14268
14269     direction_value = getHashEntry(direction_hash, direction_token);
14270
14271     if (direction_value != NULL)        // direction found
14272     {
14273       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14274                          &num_list_entries);
14275
14276       free(element_token);
14277
14278       continue;
14279     }
14280
14281     if (strchr(action_token + 1, '.') == NULL)
14282     {
14283       // no further suffixes found -- this is not an action nor direction
14284
14285       element_value = getHashEntry(element_hash, list->token);
14286       if (element_value != NULL)        // combined element found
14287         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14288                            &num_list_entries);
14289       else
14290         print_unknown_token(filename, list->token, num_unknown_tokens++);
14291
14292       free(element_token);
14293
14294       continue;
14295     }
14296
14297     // token has format "<prefix>.<suffix>.<something>"
14298
14299     direction_token = strchr(action_token + 1, '.');
14300
14301     action_token = getStringCopy(action_token);
14302     *strchr(action_token + 1, '.') = '\0';
14303
14304     action_value = getHashEntry(action_hash, action_token);
14305
14306     if (action_value == NULL)           // this is no action
14307     {
14308       element_value = getHashEntry(element_hash, list->token);
14309       if (element_value != NULL)        // combined element found
14310         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14311                            &num_list_entries);
14312       else
14313         print_unknown_token(filename, list->token, num_unknown_tokens++);
14314
14315       free(element_token);
14316       free(action_token);
14317
14318       continue;
14319     }
14320
14321     direction_value = getHashEntry(direction_hash, direction_token);
14322
14323     if (direction_value != NULL)        // direction found
14324     {
14325       add_helpanim_entry(atoi(element_value), atoi(action_value),
14326                          atoi(direction_value), delay, &num_list_entries);
14327
14328       free(element_token);
14329       free(action_token);
14330
14331       continue;
14332     }
14333
14334     // this is no direction
14335
14336     element_value = getHashEntry(element_hash, list->token);
14337     if (element_value != NULL)          // combined element found
14338       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14339                          &num_list_entries);
14340     else
14341       print_unknown_token(filename, list->token, num_unknown_tokens++);
14342
14343     free(element_token);
14344     free(action_token);
14345   }
14346
14347   print_unknown_token_end(num_unknown_tokens);
14348
14349   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14350   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14351
14352   freeSetupFileList(setup_file_list);
14353   freeSetupFileHash(element_hash);
14354   freeSetupFileHash(action_hash);
14355   freeSetupFileHash(direction_hash);
14356
14357 #if 0
14358   for (i = 0; i < num_list_entries; i++)
14359     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14360           EL_NAME(helpanim_info[i].element),
14361           helpanim_info[i].element,
14362           helpanim_info[i].action,
14363           helpanim_info[i].direction,
14364           helpanim_info[i].delay);
14365 #endif
14366 }
14367
14368 void LoadHelpTextInfo(void)
14369 {
14370   char *filename = getHelpTextFilename();
14371   int i;
14372
14373   if (helptext_info != NULL)
14374   {
14375     freeSetupFileHash(helptext_info);
14376     helptext_info = NULL;
14377   }
14378
14379   if (fileExists(filename))
14380     helptext_info = loadSetupFileHash(filename);
14381
14382   if (helptext_info == NULL)
14383   {
14384     // use reliable default values from static configuration
14385     helptext_info = newSetupFileHash();
14386
14387     for (i = 0; helptext_config[i].token; i++)
14388       setHashEntry(helptext_info,
14389                    helptext_config[i].token,
14390                    helptext_config[i].value);
14391   }
14392
14393 #if 0
14394   BEGIN_HASH_ITERATION(helptext_info, itr)
14395   {
14396     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14397           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14398   }
14399   END_HASH_ITERATION(hash, itr)
14400 #endif
14401 }
14402
14403
14404 // ----------------------------------------------------------------------------
14405 // convert levels
14406 // ----------------------------------------------------------------------------
14407
14408 #define MAX_NUM_CONVERT_LEVELS          1000
14409
14410 void ConvertLevels(void)
14411 {
14412   static LevelDirTree *convert_leveldir = NULL;
14413   static int convert_level_nr = -1;
14414   static int num_levels_handled = 0;
14415   static int num_levels_converted = 0;
14416   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14417   int i;
14418
14419   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14420                                                global.convert_leveldir);
14421
14422   if (convert_leveldir == NULL)
14423     Fail("no such level identifier: '%s'", global.convert_leveldir);
14424
14425   leveldir_current = convert_leveldir;
14426
14427   if (global.convert_level_nr != -1)
14428   {
14429     convert_leveldir->first_level = global.convert_level_nr;
14430     convert_leveldir->last_level  = global.convert_level_nr;
14431   }
14432
14433   convert_level_nr = convert_leveldir->first_level;
14434
14435   PrintLine("=", 79);
14436   Print("Converting levels\n");
14437   PrintLine("-", 79);
14438   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14439   Print("Level series name:       '%s'\n", convert_leveldir->name);
14440   Print("Level series author:     '%s'\n", convert_leveldir->author);
14441   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14442   PrintLine("=", 79);
14443   Print("\n");
14444
14445   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14446     levels_failed[i] = FALSE;
14447
14448   while (convert_level_nr <= convert_leveldir->last_level)
14449   {
14450     char *level_filename;
14451     boolean new_level;
14452
14453     level_nr = convert_level_nr++;
14454
14455     Print("Level %03d: ", level_nr);
14456
14457     LoadLevel(level_nr);
14458     if (level.no_level_file || level.no_valid_file)
14459     {
14460       Print("(no level)\n");
14461       continue;
14462     }
14463
14464     Print("converting level ... ");
14465
14466 #if 0
14467     // special case: conversion of some EMC levels as requested by ACME
14468     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14469 #endif
14470
14471     level_filename = getDefaultLevelFilename(level_nr);
14472     new_level = !fileExists(level_filename);
14473
14474     if (new_level)
14475     {
14476       SaveLevel(level_nr);
14477
14478       num_levels_converted++;
14479
14480       Print("converted.\n");
14481     }
14482     else
14483     {
14484       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14485         levels_failed[level_nr] = TRUE;
14486
14487       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14488     }
14489
14490     num_levels_handled++;
14491   }
14492
14493   Print("\n");
14494   PrintLine("=", 79);
14495   Print("Number of levels handled: %d\n", num_levels_handled);
14496   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14497          (num_levels_handled ?
14498           num_levels_converted * 100 / num_levels_handled : 0));
14499   PrintLine("-", 79);
14500   Print("Summary (for automatic parsing by scripts):\n");
14501   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14502          convert_leveldir->identifier, num_levels_converted,
14503          num_levels_handled,
14504          (num_levels_handled ?
14505           num_levels_converted * 100 / num_levels_handled : 0));
14506
14507   if (num_levels_handled != num_levels_converted)
14508   {
14509     Print(", FAILED:");
14510     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14511       if (levels_failed[i])
14512         Print(" %03d", i);
14513   }
14514
14515   Print("\n");
14516   PrintLine("=", 79);
14517
14518   CloseAllAndExit(0);
14519 }
14520
14521
14522 // ----------------------------------------------------------------------------
14523 // create and save images for use in level sketches (raw BMP format)
14524 // ----------------------------------------------------------------------------
14525
14526 void CreateLevelSketchImages(void)
14527 {
14528   Bitmap *bitmap1;
14529   Bitmap *bitmap2;
14530   int i;
14531
14532   InitElementPropertiesGfxElement();
14533
14534   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14535   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14536
14537   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14538   {
14539     int element = getMappedElement(i);
14540     char basename1[16];
14541     char basename2[16];
14542     char *filename1;
14543     char *filename2;
14544
14545     sprintf(basename1, "%04d.bmp", i);
14546     sprintf(basename2, "%04ds.bmp", i);
14547
14548     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14549     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14550
14551     DrawSizedElement(0, 0, element, TILESIZE);
14552     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14553
14554     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14555       Fail("cannot save level sketch image file '%s'", filename1);
14556
14557     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14558     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14559
14560     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14561       Fail("cannot save level sketch image file '%s'", filename2);
14562
14563     free(filename1);
14564     free(filename2);
14565
14566     // create corresponding SQL statements (for normal and small images)
14567     if (i < 1000)
14568     {
14569       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14570       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14571     }
14572
14573     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14574     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14575
14576     // optional: create content for forum level sketch demonstration post
14577     if (options.debug)
14578       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14579   }
14580
14581   FreeBitmap(bitmap1);
14582   FreeBitmap(bitmap2);
14583
14584   if (options.debug)
14585     fprintf(stderr, "\n");
14586
14587   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14588
14589   CloseAllAndExit(0);
14590 }
14591
14592
14593 // ----------------------------------------------------------------------------
14594 // create and save images for element collecting animations (raw BMP format)
14595 // ----------------------------------------------------------------------------
14596
14597 static boolean createCollectImage(int element)
14598 {
14599   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14600 }
14601
14602 void CreateCollectElementImages(void)
14603 {
14604   int i, j;
14605   int num_steps = 8;
14606   int anim_frames = num_steps - 1;
14607   int tile_size = TILESIZE;
14608   int anim_width  = tile_size * anim_frames;
14609   int anim_height = tile_size;
14610   int num_collect_images = 0;
14611   int pos_collect_images = 0;
14612
14613   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14614     if (createCollectImage(i))
14615       num_collect_images++;
14616
14617   Info("Creating %d element collecting animation images ...",
14618        num_collect_images);
14619
14620   int dst_width  = anim_width * 2;
14621   int dst_height = anim_height * num_collect_images / 2;
14622   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14623   char *basename_bmp = "RocksCollect.bmp";
14624   char *basename_png = "RocksCollect.png";
14625   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14626   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14627   int len_filename_bmp = strlen(filename_bmp);
14628   int len_filename_png = strlen(filename_png);
14629   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14630   char cmd_convert[max_command_len];
14631
14632   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14633            filename_bmp,
14634            filename_png);
14635
14636   // force using RGBA surface for destination bitmap
14637   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14638                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14639
14640   dst_bitmap->surface =
14641     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14642
14643   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14644   {
14645     if (!createCollectImage(i))
14646       continue;
14647
14648     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14649     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14650     int graphic = el2img(i);
14651     char *token_name = element_info[i].token_name;
14652     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14653     Bitmap *src_bitmap;
14654     int src_x, src_y;
14655
14656     Info("- creating collecting image for '%s' ...", token_name);
14657
14658     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14659
14660     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14661                tile_size, tile_size, 0, 0);
14662
14663     // force using RGBA surface for temporary bitmap (using transparent black)
14664     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14665                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14666
14667     tmp_bitmap->surface =
14668       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14669
14670     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14671
14672     for (j = 0; j < anim_frames; j++)
14673     {
14674       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14675       int frame_size = frame_size_final * num_steps;
14676       int offset = (tile_size - frame_size_final) / 2;
14677       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14678
14679       while (frame_size > frame_size_final)
14680       {
14681         frame_size /= 2;
14682
14683         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14684
14685         FreeBitmap(frame_bitmap);
14686
14687         frame_bitmap = half_bitmap;
14688       }
14689
14690       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14691                        frame_size_final, frame_size_final,
14692                        dst_x + j * tile_size + offset, dst_y + offset);
14693
14694       FreeBitmap(frame_bitmap);
14695     }
14696
14697     tmp_bitmap->surface_masked = NULL;
14698
14699     FreeBitmap(tmp_bitmap);
14700
14701     pos_collect_images++;
14702   }
14703
14704   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14705     Fail("cannot save element collecting image file '%s'", filename_bmp);
14706
14707   FreeBitmap(dst_bitmap);
14708
14709   Info("Converting image file from BMP to PNG ...");
14710
14711   if (system(cmd_convert) != 0)
14712     Fail("converting image file failed");
14713
14714   unlink(filename_bmp);
14715
14716   Info("Done.");
14717
14718   CloseAllAndExit(0);
14719 }
14720
14721
14722 // ----------------------------------------------------------------------------
14723 // create and save images for custom and group elements (raw BMP format)
14724 // ----------------------------------------------------------------------------
14725
14726 void CreateCustomElementImages(char *directory)
14727 {
14728   char *src_basename = "RocksCE-template.ilbm";
14729   char *dst_basename = "RocksCE.bmp";
14730   char *src_filename = getPath2(directory, src_basename);
14731   char *dst_filename = getPath2(directory, dst_basename);
14732   Bitmap *src_bitmap;
14733   Bitmap *bitmap;
14734   int yoffset_ce = 0;
14735   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14736   int i;
14737
14738   InitVideoDefaults();
14739
14740   ReCreateBitmap(&backbuffer, video.width, video.height);
14741
14742   src_bitmap = LoadImage(src_filename);
14743
14744   bitmap = CreateBitmap(TILEX * 16 * 2,
14745                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14746                         DEFAULT_DEPTH);
14747
14748   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14749   {
14750     int x = i % 16;
14751     int y = i / 16;
14752     int ii = i + 1;
14753     int j;
14754
14755     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14756                TILEX * x, TILEY * y + yoffset_ce);
14757
14758     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14759                TILEX, TILEY,
14760                TILEX * x + TILEX * 16,
14761                TILEY * y + yoffset_ce);
14762
14763     for (j = 2; j >= 0; j--)
14764     {
14765       int c = ii % 10;
14766
14767       BlitBitmap(src_bitmap, bitmap,
14768                  TILEX + c * 7, 0, 6, 10,
14769                  TILEX * x + 6 + j * 7,
14770                  TILEY * y + 11 + yoffset_ce);
14771
14772       BlitBitmap(src_bitmap, bitmap,
14773                  TILEX + c * 8, TILEY, 6, 10,
14774                  TILEX * 16 + TILEX * x + 6 + j * 8,
14775                  TILEY * y + 10 + yoffset_ce);
14776
14777       ii /= 10;
14778     }
14779   }
14780
14781   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14782   {
14783     int x = i % 16;
14784     int y = i / 16;
14785     int ii = i + 1;
14786     int j;
14787
14788     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14789                TILEX * x, TILEY * y + yoffset_ge);
14790
14791     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14792                TILEX, TILEY,
14793                TILEX * x + TILEX * 16,
14794                TILEY * y + yoffset_ge);
14795
14796     for (j = 1; j >= 0; j--)
14797     {
14798       int c = ii % 10;
14799
14800       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14801                  TILEX * x + 6 + j * 10,
14802                  TILEY * y + 11 + yoffset_ge);
14803
14804       BlitBitmap(src_bitmap, bitmap,
14805                  TILEX + c * 8, TILEY + 12, 6, 10,
14806                  TILEX * 16 + TILEX * x + 10 + j * 8,
14807                  TILEY * y + 10 + yoffset_ge);
14808
14809       ii /= 10;
14810     }
14811   }
14812
14813   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14814     Fail("cannot save CE graphics file '%s'", dst_filename);
14815
14816   FreeBitmap(bitmap);
14817
14818   CloseAllAndExit(0);
14819 }