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