added support for creature movement settings in BD engine to level editor
[rocksndiamonds.git] / src / files.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // files.c
10 // ============================================================================
11
12 #include <ctype.h>
13 #include <sys/stat.h>
14 #include <dirent.h>
15 #include <math.h>
16
17 #include "libgame/libgame.h"
18
19 #include "files.h"
20 #include "init.h"
21 #include "screens.h"
22 #include "tools.h"
23 #include "tape.h"
24 #include "config.h"
25 #include "api.h"
26
27 #define ENABLE_UNUSED_CODE      0       // currently unused functions
28 #define ENABLE_HISTORIC_CHUNKS  0       // only for historic reference
29 #define ENABLE_RESERVED_CODE    0       // reserved for later use
30
31 #define CHUNK_ID_LEN            4       // IFF style chunk id length
32 #define CHUNK_SIZE_UNDEFINED    0       // undefined chunk size == 0
33 #define CHUNK_SIZE_NONE         -1      // do not write chunk size
34
35 #define LEVEL_CHUNK_NAME_SIZE   MAX_LEVEL_NAME_LEN
36 #define LEVEL_CHUNK_AUTH_SIZE   MAX_LEVEL_AUTHOR_LEN
37
38 #define LEVEL_CHUNK_VERS_SIZE   8       // size of file version chunk
39 #define LEVEL_CHUNK_DATE_SIZE   4       // size of file date chunk
40 #define LEVEL_CHUNK_HEAD_SIZE   80      // size of level file header
41 #define LEVEL_CHUNK_HEAD_UNUSED 0       // unused level header bytes
42 #define LEVEL_CHUNK_CNT2_SIZE   160     // size of level CNT2 chunk
43 #define LEVEL_CHUNK_CNT2_UNUSED 11      // unused CNT2 chunk bytes
44 #define LEVEL_CHUNK_CNT3_HEADER 16      // size of level CNT3 header
45 #define LEVEL_CHUNK_CNT3_UNUSED 10      // unused CNT3 chunk bytes
46 #define LEVEL_CPART_CUS3_SIZE   134     // size of CUS3 chunk part
47 #define LEVEL_CPART_CUS3_UNUSED 15      // unused CUS3 bytes / part
48 #define LEVEL_CHUNK_GRP1_SIZE   74      // size of level GRP1 chunk
49
50 // (element number, number of change pages, change page number)
51 #define LEVEL_CHUNK_CUSX_UNCHANGED      (2 + (1 + 1) + (1 + 1))
52
53 // (element number only)
54 #define LEVEL_CHUNK_GRPX_UNCHANGED      2
55 #define LEVEL_CHUNK_EMPX_UNCHANGED      2
56 #define LEVEL_CHUNK_NOTE_UNCHANGED      2
57
58 // (nothing at all if unchanged)
59 #define LEVEL_CHUNK_ELEM_UNCHANGED      0
60
61 #define TAPE_CHUNK_VERS_SIZE    8       // size of file version chunk
62 #define TAPE_CHUNK_HEAD_SIZE    20      // size of tape file header
63 #define TAPE_CHUNK_SCRN_SIZE    2       // size of screen size chunk
64
65 #define SCORE_CHUNK_VERS_SIZE   8       // size of file version chunk
66
67 #define LEVEL_CHUNK_CNT3_SIZE(x)         (LEVEL_CHUNK_CNT3_HEADER + (x))
68 #define LEVEL_CHUNK_CUS3_SIZE(x)         (2 + (x) * LEVEL_CPART_CUS3_SIZE)
69 #define LEVEL_CHUNK_CUS4_SIZE(x)         (96 + (x) * 48)
70
71 // file identifier strings
72 #define LEVEL_COOKIE_TMPL               "ROCKSNDIAMONDS_LEVEL_FILE_VERSION_x.x"
73 #define TAPE_COOKIE_TMPL                "ROCKSNDIAMONDS_TAPE_FILE_VERSION_x.x"
74 #define SCORE_COOKIE_TMPL               "ROCKSNDIAMONDS_SCORE_FILE_VERSION_x.x"
75
76 // values for deciding when (not) to save configuration data
77 #define SAVE_CONF_NEVER                 0
78 #define SAVE_CONF_ALWAYS                1
79 #define SAVE_CONF_WHEN_CHANGED          -1
80
81 // values for chunks using micro chunks
82 #define CONF_MASK_1_BYTE                0x00
83 #define CONF_MASK_2_BYTE                0x40
84 #define CONF_MASK_4_BYTE                0x80
85 #define CONF_MASK_MULTI_BYTES           0xc0
86
87 #define CONF_MASK_BYTES                 0xc0
88 #define CONF_MASK_TOKEN                 0x3f
89
90 #define CONF_VALUE_1_BYTE(x)            (CONF_MASK_1_BYTE       | (x))
91 #define CONF_VALUE_2_BYTE(x)            (CONF_MASK_2_BYTE       | (x))
92 #define CONF_VALUE_4_BYTE(x)            (CONF_MASK_4_BYTE       | (x))
93 #define CONF_VALUE_MULTI_BYTES(x)       (CONF_MASK_MULTI_BYTES  | (x))
94
95 // these definitions are just for convenience of use and readability
96 #define CONF_VALUE_8_BIT(x)             CONF_VALUE_1_BYTE(x)
97 #define CONF_VALUE_16_BIT(x)            CONF_VALUE_2_BYTE(x)
98 #define CONF_VALUE_32_BIT(x)            CONF_VALUE_4_BYTE(x)
99 #define CONF_VALUE_BYTES(x)             CONF_VALUE_MULTI_BYTES(x)
100
101 #define CONF_VALUE_NUM_BYTES(x)         ((x) == CONF_MASK_1_BYTE ? 1 :  \
102                                          (x) == CONF_MASK_2_BYTE ? 2 :  \
103                                          (x) == CONF_MASK_4_BYTE ? 4 : 0)
104
105 #define CONF_CONTENT_NUM_ELEMENTS       (3 * 3)
106 #define CONF_CONTENT_NUM_BYTES          (CONF_CONTENT_NUM_ELEMENTS * 2)
107 #define CONF_ELEMENT_NUM_BYTES          (2)
108
109 #define CONF_ENTITY_NUM_BYTES(t)        ((t) == TYPE_ELEMENT ||         \
110                                          (t) == TYPE_ELEMENT_LIST ?     \
111                                          CONF_ELEMENT_NUM_BYTES :       \
112                                          (t) == TYPE_CONTENT ||         \
113                                          (t) == TYPE_CONTENT_LIST ?     \
114                                          CONF_CONTENT_NUM_BYTES : 1)
115
116 #define CONF_ELEMENT_BYTE_POS(i)        ((i) * CONF_ELEMENT_NUM_BYTES)
117 #define CONF_ELEMENTS_ELEMENT(b, i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
118                                         (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
119
120 #define CONF_CONTENT_ELEMENT_POS(c,x,y) ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
121                                          (y) * 3 + (x))
122 #define CONF_CONTENT_BYTE_POS(c,x,y)    (CONF_CONTENT_ELEMENT_POS(c,x,y) *    \
123                                          CONF_ELEMENT_NUM_BYTES)
124 #define CONF_CONTENTS_ELEMENT(b,c,x,y) ((b[CONF_CONTENT_BYTE_POS(c,x,y)]<< 8)|\
125                                         (b[CONF_CONTENT_BYTE_POS(c,x,y) + 1]))
126
127 // temporary variables used to store pointers to structure members
128 static struct LevelInfo li;
129 static struct ElementInfo xx_ei, yy_ei;
130 static struct ElementChangeInfo xx_change;
131 static struct ElementGroupInfo xx_group;
132 static struct EnvelopeInfo xx_envelope;
133 static unsigned int xx_event_bits[NUM_CE_BITFIELDS];
134 static char xx_default_description[MAX_ELEMENT_NAME_LEN + 1];
135 static int xx_num_contents;
136 static int xx_current_change_page;
137 static char xx_default_string_empty[1] = "";
138 static int xx_string_length_unused;
139
140 struct LevelFileConfigInfo
141 {
142   int element;                  // element for which data is to be stored
143   int save_type;                // save data always, never or when changed
144   int data_type;                // data type (used internally, not stored)
145   int conf_type;                // micro chunk identifier (stored in file)
146
147   // (mandatory)
148   void *value;                  // variable that holds the data to be stored
149   int default_value;            // initial default value for this variable
150
151   // (optional)
152   void *value_copy;             // variable that holds the data to be copied
153   void *num_entities;           // number of entities for multi-byte data
154   int default_num_entities;     // default number of entities for this data
155   int max_num_entities;         // maximal number of entities for this data
156   char *default_string;         // optional default string for string data
157 };
158
159 static struct LevelFileConfigInfo chunk_config_INFO[] =
160 {
161   // ---------- values not related to single elements -------------------------
162
163   {
164     -1,                                 SAVE_CONF_ALWAYS,
165     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
166     &li.game_engine_type,               GAME_ENGINE_TYPE_RND
167   },
168   {
169     -1,                                 SAVE_CONF_ALWAYS,
170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
171     &li.fieldx,                         STD_LEV_FIELDX
172   },
173   {
174     -1,                                 SAVE_CONF_ALWAYS,
175     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
176     &li.fieldy,                         STD_LEV_FIELDY
177   },
178   {
179     -1,                                 SAVE_CONF_ALWAYS,
180     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
181     &li.time,                           100
182   },
183   {
184     -1,                                 SAVE_CONF_ALWAYS,
185     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
186     &li.gems_needed,                    0
187   },
188   {
189     -1,                                 -1,
190     TYPE_INTEGER,                       CONF_VALUE_32_BIT(2),
191     &li.random_seed,                    0
192   },
193   {
194     -1,                                 -1,
195     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
196     &li.use_step_counter,               FALSE
197   },
198   {
199     -1,                                 -1,
200     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
201     &li.wind_direction_initial,         MV_NONE
202   },
203   {
204     -1,                                 -1,
205     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
206     &li.em_slippery_gems,               FALSE
207   },
208   {
209     -1,                                 -1,
210     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
211     &li.use_custom_template,            FALSE
212   },
213   {
214     -1,                                 -1,
215     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
216     &li.can_move_into_acid_bits,        ~0      // default: everything can
217   },
218   {
219     -1,                                 -1,
220     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(7),
221     &li.dont_collide_with_bits,         ~0      // default: always deadly
222   },
223   {
224     -1,                                 -1,
225     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
226     &li.em_explodes_by_fire,            FALSE
227   },
228   {
229     -1,                                 -1,
230     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
231     &li.score[SC_TIME_BONUS],           1
232   },
233   {
234     -1,                                 -1,
235     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
236     &li.auto_exit_sokoban,              FALSE
237   },
238   {
239     -1,                                 -1,
240     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
241     &li.auto_count_gems,                FALSE
242   },
243   {
244     -1,                                 -1,
245     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
246     &li.solved_by_one_player,           FALSE
247   },
248   {
249     -1,                                 -1,
250     TYPE_INTEGER,                       CONF_VALUE_8_BIT(12),
251     &li.time_score_base,                1
252   },
253   {
254     -1,                                 -1,
255     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
256     &li.rate_time_over_score,           FALSE
257   },
258   {
259     -1,                                 -1,
260     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
261     &li.bd_intermission,                FALSE
262   },
263   {
264     -1,                                 -1,
265     TYPE_INTEGER,                       CONF_VALUE_8_BIT(15),
266     &li.bd_scheduling_type,             GD_SCHEDULING_MILLISECONDS
267   },
268   {
269     -1,                                 -1,
270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
271     &li.bd_pal_timing,                  FALSE
272   },
273   {
274     -1,                                 -1,
275     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
276     &li.bd_cycle_delay_ms,              200
277   },
278   {
279     -1,                                 -1,
280     TYPE_INTEGER,                       CONF_VALUE_8_BIT(17),
281     &li.bd_cycle_delay_c64,             0
282   },
283   {
284     -1,                                 -1,
285     TYPE_INTEGER,                       CONF_VALUE_8_BIT(18),
286     &li.bd_hatching_delay_cycles,       21
287   },
288   {
289     -1,                                 -1,
290     TYPE_INTEGER,                       CONF_VALUE_8_BIT(19),
291     &li.bd_hatching_delay_seconds,      2
292   },
293   {
294     -1,                                 -1,
295     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(20),
296     &li.bd_line_shifting_borders,       FALSE
297   },
298   {
299     -1,                                 -1,
300     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(21),
301     &li.bd_scan_first_and_last_row,     TRUE
302   },
303   {
304     -1,                                 -1,
305     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(22),
306     &li.bd_short_explosions,            TRUE
307   },
308   {
309     -1,                                 -1,
310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(23),
311     &li.bd_gravity_affects_all,         TRUE
312   },
313   {
314     -1,                                 -1,
315     TYPE_INTEGER,                       CONF_VALUE_8_BIT(24),
316     &li.bd_cave_random_seed_c64,        0
317   },
318   {
319     -1,                                 -1,
320     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(25),
321     &li.bd_creatures_start_backwards,   FALSE
322   },
323   {
324     -1,                                 -1,
325     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(26),
326     &li.bd_creatures_turn_on_hatching,  FALSE
327   },
328   {
329     -1,                                 -1,
330     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
331     &li.bd_creatures_auto_turn_delay,   0
332   },
333
334   {
335     -1,                                 -1,
336     -1,                                 -1,
337     NULL,                               -1
338   }
339 };
340
341 static struct LevelFileConfigInfo chunk_config_ELEM[] =
342 {
343   // (these values are the same for each player)
344   {
345     EL_PLAYER_1,                        -1,
346     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
347     &li.block_last_field,               FALSE   // default case for EM levels
348   },
349   {
350     EL_PLAYER_1,                        -1,
351     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
352     &li.sp_block_last_field,            TRUE    // default case for SP levels
353   },
354   {
355     EL_PLAYER_1,                        -1,
356     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
357     &li.instant_relocation,             FALSE
358   },
359   {
360     EL_PLAYER_1,                        -1,
361     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
362     &li.can_pass_to_walkable,           FALSE
363   },
364   {
365     EL_PLAYER_1,                        -1,
366     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
367     &li.block_snap_field,               TRUE
368   },
369   {
370     EL_PLAYER_1,                        -1,
371     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
372     &li.continuous_snapping,            TRUE
373   },
374   {
375     EL_PLAYER_1,                        -1,
376     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
377     &li.shifted_relocation,             FALSE
378   },
379   {
380     EL_PLAYER_1,                        -1,
381     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(15),
382     &li.lazy_relocation,                FALSE
383   },
384   {
385     EL_PLAYER_1,                        -1,
386     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(16),
387     &li.finish_dig_collect,             TRUE
388   },
389   {
390     EL_PLAYER_1,                        -1,
391     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(17),
392     &li.keep_walkable_ce,               FALSE
393   },
394
395   // (these values are different for each player)
396   {
397     EL_PLAYER_1,                        -1,
398     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
399     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
400   },
401   {
402     EL_PLAYER_1,                        -1,
403     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
404     &li.initial_player_gravity[0],      FALSE
405   },
406   {
407     EL_PLAYER_1,                        -1,
408     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
409     &li.use_start_element[0],           FALSE
410   },
411   {
412     EL_PLAYER_1,                        -1,
413     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
414     &li.start_element[0],               EL_PLAYER_1
415   },
416   {
417     EL_PLAYER_1,                        -1,
418     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
419     &li.use_artwork_element[0],         FALSE
420   },
421   {
422     EL_PLAYER_1,                        -1,
423     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
424     &li.artwork_element[0],             EL_PLAYER_1
425   },
426   {
427     EL_PLAYER_1,                        -1,
428     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
429     &li.use_explosion_element[0],       FALSE
430   },
431   {
432     EL_PLAYER_1,                        -1,
433     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
434     &li.explosion_element[0],           EL_PLAYER_1
435   },
436   {
437     EL_PLAYER_1,                        -1,
438     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
439     &li.use_initial_inventory[0],       FALSE
440   },
441   {
442     EL_PLAYER_1,                        -1,
443     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
444     &li.initial_inventory_size[0],      1
445   },
446   {
447     EL_PLAYER_1,                        -1,
448     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
449     &li.initial_inventory_content[0][0],EL_EMPTY, NULL,
450     &li.initial_inventory_size[0],      1, MAX_INITIAL_INVENTORY_SIZE
451   },
452
453   {
454     EL_PLAYER_2,                        -1,
455     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
456     &li.initial_player_stepsize[1],     STEPSIZE_NORMAL
457   },
458   {
459     EL_PLAYER_2,                        -1,
460     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
461     &li.initial_player_gravity[1],      FALSE
462   },
463   {
464     EL_PLAYER_2,                        -1,
465     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
466     &li.use_start_element[1],           FALSE
467   },
468   {
469     EL_PLAYER_2,                        -1,
470     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
471     &li.start_element[1],               EL_PLAYER_2
472   },
473   {
474     EL_PLAYER_2,                        -1,
475     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
476     &li.use_artwork_element[1],         FALSE
477   },
478   {
479     EL_PLAYER_2,                        -1,
480     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
481     &li.artwork_element[1],             EL_PLAYER_2
482   },
483   {
484     EL_PLAYER_2,                        -1,
485     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
486     &li.use_explosion_element[1],       FALSE
487   },
488   {
489     EL_PLAYER_2,                        -1,
490     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
491     &li.explosion_element[1],           EL_PLAYER_2
492   },
493   {
494     EL_PLAYER_2,                        -1,
495     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
496     &li.use_initial_inventory[1],       FALSE
497   },
498   {
499     EL_PLAYER_2,                        -1,
500     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
501     &li.initial_inventory_size[1],      1
502   },
503   {
504     EL_PLAYER_2,                        -1,
505     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
506     &li.initial_inventory_content[1][0],EL_EMPTY, NULL,
507     &li.initial_inventory_size[1],      1, MAX_INITIAL_INVENTORY_SIZE
508   },
509
510   {
511     EL_PLAYER_3,                        -1,
512     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
513     &li.initial_player_stepsize[2],     STEPSIZE_NORMAL
514   },
515   {
516     EL_PLAYER_3,                        -1,
517     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
518     &li.initial_player_gravity[2],      FALSE
519   },
520   {
521     EL_PLAYER_3,                        -1,
522     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
523     &li.use_start_element[2],           FALSE
524   },
525   {
526     EL_PLAYER_3,                        -1,
527     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
528     &li.start_element[2],               EL_PLAYER_3
529   },
530   {
531     EL_PLAYER_3,                        -1,
532     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
533     &li.use_artwork_element[2],         FALSE
534   },
535   {
536     EL_PLAYER_3,                        -1,
537     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
538     &li.artwork_element[2],             EL_PLAYER_3
539   },
540   {
541     EL_PLAYER_3,                        -1,
542     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
543     &li.use_explosion_element[2],       FALSE
544   },
545   {
546     EL_PLAYER_3,                        -1,
547     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
548     &li.explosion_element[2],           EL_PLAYER_3
549   },
550   {
551     EL_PLAYER_3,                        -1,
552     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
553     &li.use_initial_inventory[2],       FALSE
554   },
555   {
556     EL_PLAYER_3,                        -1,
557     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
558     &li.initial_inventory_size[2],      1
559   },
560   {
561     EL_PLAYER_3,                        -1,
562     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
563     &li.initial_inventory_content[2][0],EL_EMPTY, NULL,
564     &li.initial_inventory_size[2],      1, MAX_INITIAL_INVENTORY_SIZE
565   },
566
567   {
568     EL_PLAYER_4,                        -1,
569     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
570     &li.initial_player_stepsize[3],     STEPSIZE_NORMAL
571   },
572   {
573     EL_PLAYER_4,                        -1,
574     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
575     &li.initial_player_gravity[3],      FALSE
576   },
577   {
578     EL_PLAYER_4,                        -1,
579     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
580     &li.use_start_element[3],           FALSE
581   },
582   {
583     EL_PLAYER_4,                        -1,
584     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
585     &li.start_element[3],               EL_PLAYER_4
586   },
587   {
588     EL_PLAYER_4,                        -1,
589     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
590     &li.use_artwork_element[3],         FALSE
591   },
592   {
593     EL_PLAYER_4,                        -1,
594     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
595     &li.artwork_element[3],             EL_PLAYER_4
596   },
597   {
598     EL_PLAYER_4,                        -1,
599     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
600     &li.use_explosion_element[3],       FALSE
601   },
602   {
603     EL_PLAYER_4,                        -1,
604     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
605     &li.explosion_element[3],           EL_PLAYER_4
606   },
607   {
608     EL_PLAYER_4,                        -1,
609     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
610     &li.use_initial_inventory[3],       FALSE
611   },
612   {
613     EL_PLAYER_4,                        -1,
614     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(14),
615     &li.initial_inventory_size[3],      1
616   },
617   {
618     EL_PLAYER_4,                        -1,
619     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
620     &li.initial_inventory_content[3][0],EL_EMPTY, NULL,
621     &li.initial_inventory_size[3],      1, MAX_INITIAL_INVENTORY_SIZE
622   },
623
624   // (these values are only valid for BD style levels)
625   // (some values for BD style amoeba following below)
626   {
627     EL_BD_PLAYER,                       -1,
628     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
629     &li.bd_diagonal_movements,          FALSE
630   },
631   {
632     EL_BD_PLAYER,                       -1,
633     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
634     &li.bd_topmost_player_active,       TRUE
635   },
636   {
637     EL_BD_PLAYER,                       -1,
638     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
639     &li.bd_pushing_prob,                25
640   },
641   {
642     EL_BD_PLAYER,                       -1,
643     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
644     &li.bd_pushing_prob_with_sweet,     100
645   },
646   {
647     EL_BD_PLAYER,                       -1,
648     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
649     &li.bd_push_mega_rock_with_sweet,   FALSE
650   },
651   {
652     EL_BD_PLAYER,                       -1,
653     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
654     &li.bd_snap_element,                EL_EMPTY
655   },
656
657   {
658     EL_BD_DIAMOND,                      -1,
659     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
660     &li.score[SC_DIAMOND_EXTRA],        20
661   },
662
663   {
664     EL_BD_MAGIC_WALL,                   -1,
665     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
666     &li.bd_magic_wall_wait_hatching,    FALSE
667   },
668   {
669     EL_BD_MAGIC_WALL,                   -1,
670     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
671     &li.bd_magic_wall_stops_amoeba,     TRUE
672   },
673   {
674     EL_BD_MAGIC_WALL,                   -1,
675     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
676     &li.bd_magic_wall_diamond_to,       EL_BD_ROCK_FALLING
677   },
678   {
679     EL_BD_MAGIC_WALL,                   -1,
680     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
681     &li.bd_magic_wall_rock_to,          EL_BD_DIAMOND_FALLING
682   },
683   {
684     EL_BD_MAGIC_WALL,                   -1,
685     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
686     &li.bd_magic_wall_mega_rock_to,     EL_BD_NITRO_PACK_FALLING
687   },
688   {
689     EL_BD_MAGIC_WALL,                   -1,
690     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
691     &li.bd_magic_wall_nut_to,           EL_BD_NUT_FALLING
692   },
693   {
694     EL_BD_MAGIC_WALL,                   -1,
695     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
696     &li.bd_magic_wall_nitro_pack_to,    EL_BD_MEGA_ROCK_FALLING
697   },
698   {
699     EL_BD_MAGIC_WALL,                   -1,
700     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
701     &li.bd_magic_wall_flying_diamond_to, EL_BD_FLYING_ROCK_FLYING
702   },
703   {
704     EL_BD_MAGIC_WALL,                   -1,
705     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
706     &li.bd_magic_wall_flying_rock_to,   EL_BD_FLYING_DIAMOND_FLYING
707   },
708
709   {
710     EL_BD_CLOCK,                        -1,
711     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
712     &li.bd_clock_extra_time,            30
713   },
714
715   {
716     EL_BD_VOODOO_DOLL,                  -1,
717     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
718     &li.bd_voodoo_collects_diamonds,    FALSE
719   },
720   {
721     EL_BD_VOODOO_DOLL,                  -1,
722     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
723     &li.bd_voodoo_hurt_kills_player,    FALSE
724   },
725   {
726     EL_BD_VOODOO_DOLL,                  -1,
727     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
728     &li.bd_voodoo_dies_by_rock,         FALSE
729   },
730   {
731     EL_BD_VOODOO_DOLL,                  -1,
732     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
733     &li.bd_voodoo_vanish_by_explosion,  TRUE
734   },
735   {
736     EL_BD_VOODOO_DOLL,                  -1,
737     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
738     &li.bd_voodoo_penalty_time,         30
739   },
740
741   {
742     EL_BD_SLIME,                        -1,
743     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
744     &li.bd_slime_is_predictable,        TRUE
745   },
746   {
747     EL_BD_SLIME,                        -1,
748     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
749     &li.bd_slime_permeability_rate,     100
750   },
751   {
752     EL_BD_SLIME,                        -1,
753     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
754     &li.bd_slime_permeability_bits_c64, 0
755   },
756   {
757     EL_BD_SLIME,                        -1,
758     TYPE_INTEGER,                       CONF_VALUE_32_BIT(1),
759     &li.bd_slime_random_seed_c64,       -1
760   },
761   {
762     EL_BD_SLIME,                        -1,
763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
764     &li.bd_slime_eats_element_1,        EL_BD_DIAMOND
765   },
766   {
767     EL_BD_SLIME,                        -1,
768     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
769     &li.bd_slime_converts_to_element_1, EL_BD_DIAMOND_FALLING
770   },
771   {
772     EL_BD_SLIME,                        -1,
773     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
774     &li.bd_slime_eats_element_2,        EL_BD_ROCK
775   },
776   {
777     EL_BD_SLIME,                        -1,
778     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(4),
779     &li.bd_slime_converts_to_element_2, EL_BD_ROCK_FALLING
780   },
781   {
782     EL_BD_SLIME,                        -1,
783     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
784     &li.bd_slime_eats_element_3,        EL_BD_NUT
785   },
786   {
787     EL_BD_SLIME,                        -1,
788     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
789     &li.bd_slime_converts_to_element_3, EL_BD_NUT_FALLING
790   },
791
792   {
793     EL_BD_ACID,                         -1,
794     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
795     &li.bd_acid_eats_element,           EL_BD_SAND
796   },
797   {
798     EL_BD_ACID,                         -1,
799     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
800     &li.bd_acid_spread_rate,            3
801   },
802   {
803     EL_BD_ACID,                         -1,
804     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
805     &li.bd_acid_turns_to_element,       EL_BD_EXPLODING_3
806   },
807
808   {
809     EL_BD_BITER,                        -1,
810     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
811     &li.bd_biter_move_delay,            0
812   },
813   {
814     EL_BD_BITER,                        -1,
815     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
816     &li.bd_biter_eats_element,          EL_BD_DIAMOND
817   },
818
819   {
820     EL_BD_BLADDER,                      -1,
821     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
822     &li.bd_bladder_converts_by_element, EL_BD_VOODOO_DOLL
823   },
824
825   {
826     EL_BD_EXPANDABLE_WALL_ANY,          -1,
827     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
828     &li.bd_change_expanding_wall,       FALSE
829   },
830   {
831     EL_BD_EXPANDABLE_WALL_ANY,          -1,
832     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
833     &li.bd_expanding_wall_looks_like,   EL_BD_WALL
834   },
835
836   {
837     EL_BD_REPLICATOR,                   -1,
838     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
839     &li.bd_replicators_active,          TRUE
840   },
841   {
842     EL_BD_REPLICATOR,                   -1,
843     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
844     &li.bd_replicator_create_delay,     4
845   },
846
847   {
848     EL_BD_CONVEYOR_LEFT,                -1,
849     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
850     &li.bd_conveyor_belts_active,       TRUE
851   },
852   {
853     EL_BD_CONVEYOR_LEFT,                -1,
854     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
855     &li.bd_conveyor_belts_changed,      FALSE
856   },
857
858   {
859     EL_BD_WATER,                        -1,
860     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
861     &li.bd_water_cannot_flow_down,      FALSE
862   },
863
864   {
865     EL_BD_NUT,                          -1,
866     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
867     &li.bd_nut_content,                 EL_BD_NUT_BREAKING_1
868   },
869
870   {
871     EL_BD_PNEUMATIC_HAMMER,             -1,
872     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
873     &li.bd_hammer_walls_break_delay,    5
874   },
875   {
876     EL_BD_PNEUMATIC_HAMMER,             -1,
877     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
878     &li.bd_hammer_walls_reappear,       FALSE
879   },
880   {
881     EL_BD_PNEUMATIC_HAMMER,             -1,
882     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
883     &li.bd_hammer_walls_reappear_delay, 100
884   },
885
886   {
887     EL_BD_SKELETON,                     -1,
888     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
889     &li.bd_num_skeletons_needed_for_pot, 5
890   },
891   {
892     EL_BD_SKELETON,                     -1,
893     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
894     &li.bd_skeleton_worth_num_diamonds, 0
895   },
896
897   {
898     EL_BD_SAND,                         -1,
899     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
900     &li.bd_sand_looks_like,             EL_BD_SAND
901   },
902
903   // (the following values are related to various game elements)
904
905   {
906     EL_EMERALD,                         -1,
907     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
908     &li.score[SC_EMERALD],              10
909   },
910
911   {
912     EL_DIAMOND,                         -1,
913     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
914     &li.score[SC_DIAMOND],              10
915   },
916
917   {
918     EL_BUG,                             -1,
919     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
920     &li.score[SC_BUG],                  10
921   },
922
923   {
924     EL_SPACESHIP,                       -1,
925     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
926     &li.score[SC_SPACESHIP],            10
927   },
928
929   {
930     EL_PACMAN,                          -1,
931     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
932     &li.score[SC_PACMAN],               10
933   },
934
935   {
936     EL_NUT,                             -1,
937     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
938     &li.score[SC_NUT],                  10
939   },
940
941   {
942     EL_DYNAMITE,                        -1,
943     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
944     &li.score[SC_DYNAMITE],             10
945   },
946
947   {
948     EL_KEY_1,                           -1,
949     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
950     &li.score[SC_KEY],                  10
951   },
952
953   {
954     EL_PEARL,                           -1,
955     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
956     &li.score[SC_PEARL],                10
957   },
958
959   {
960     EL_CRYSTAL,                         -1,
961     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
962     &li.score[SC_CRYSTAL],              10
963   },
964
965   // (amoeba values used by R'n'D game engine only)
966   {
967     EL_BD_AMOEBA,                       -1,
968     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
969     &li.amoeba_content,                 EL_DIAMOND
970   },
971   {
972     EL_BD_AMOEBA,                       -1,
973     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
974     &li.amoeba_speed,                   10
975   },
976   {
977     EL_BD_AMOEBA,                       -1,
978     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
979     &li.grow_into_diggable,             TRUE
980   },
981   // (amoeba values used by BD game engine only)
982   {
983     EL_BD_AMOEBA,                       -1,
984     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
985     &li.bd_amoeba_wait_for_hatching,    FALSE
986   },
987   {
988     EL_BD_AMOEBA,                       -1,
989     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
990     &li.bd_amoeba_start_immediately,    TRUE
991   },
992   {
993     EL_BD_AMOEBA,                       -1,
994     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
995     &li.bd_amoeba_2_explode_by_amoeba,  TRUE
996   },
997   {
998     EL_BD_AMOEBA,                       -1,
999     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1000     &li.bd_amoeba_threshold_too_big,    200
1001   },
1002   {
1003     EL_BD_AMOEBA,                       -1,
1004     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1005     &li.bd_amoeba_slow_growth_time,     200
1006   },
1007   {
1008     EL_BD_AMOEBA,                       -1,
1009     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1010     &li.bd_amoeba_slow_growth_rate,     3
1011   },
1012   {
1013     EL_BD_AMOEBA,                       -1,
1014     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1015     &li.bd_amoeba_fast_growth_rate,     25
1016   },
1017   {
1018     EL_BD_AMOEBA,                       -1,
1019     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1020     &li.bd_amoeba_content_too_big,      EL_BD_ROCK
1021   },
1022   {
1023     EL_BD_AMOEBA,                       -1,
1024     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1025     &li.bd_amoeba_content_enclosed,     EL_BD_DIAMOND
1026   },
1027
1028   {
1029     EL_BD_AMOEBA_2,                     -1,
1030     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1031     &li.bd_amoeba_2_threshold_too_big,  200
1032   },
1033   {
1034     EL_BD_AMOEBA_2,                     -1,
1035     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1036     &li.bd_amoeba_2_slow_growth_time,   200
1037   },
1038   {
1039     EL_BD_AMOEBA_2,                     -1,
1040     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1041     &li.bd_amoeba_2_slow_growth_rate,   3
1042   },
1043   {
1044     EL_BD_AMOEBA_2,                     -1,
1045     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1046     &li.bd_amoeba_2_fast_growth_rate,   25
1047   },
1048   {
1049     EL_BD_AMOEBA_2,                     -1,
1050     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1051     &li.bd_amoeba_2_content_too_big,    EL_BD_ROCK
1052   },
1053   {
1054     EL_BD_AMOEBA_2,                     -1,
1055     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(6),
1056     &li.bd_amoeba_2_content_enclosed,   EL_BD_DIAMOND
1057   },
1058   {
1059     EL_BD_AMOEBA_2,                     -1,
1060     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1061     &li.bd_amoeba_2_content_exploding,  EL_EMPTY
1062   },
1063   {
1064     EL_BD_AMOEBA_2,                     -1,
1065     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(8),
1066     &li.bd_amoeba_2_content_looks_like, EL_BD_AMOEBA_2
1067   },
1068
1069   {
1070     EL_YAMYAM,                          -1,
1071     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1072     &li.yamyam_content,                 EL_ROCK, NULL,
1073     &li.num_yamyam_contents,            4, MAX_ELEMENT_CONTENTS
1074   },
1075   {
1076     EL_YAMYAM,                          -1,
1077     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1078     &li.score[SC_YAMYAM],               10
1079   },
1080
1081   {
1082     EL_ROBOT,                           -1,
1083     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1084     &li.score[SC_ROBOT],                10
1085   },
1086   {
1087     EL_ROBOT,                           -1,
1088     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1089     &li.slurp_score,                    10
1090   },
1091
1092   {
1093     EL_ROBOT_WHEEL,                     -1,
1094     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1095     &li.time_wheel,                     10
1096   },
1097
1098   {
1099     EL_MAGIC_WALL,                      -1,
1100     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1101     &li.time_magic_wall,                10
1102   },
1103
1104   {
1105     EL_GAME_OF_LIFE,                    -1,
1106     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1107     &li.game_of_life[0],                2
1108   },
1109   {
1110     EL_GAME_OF_LIFE,                    -1,
1111     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1112     &li.game_of_life[1],                3
1113   },
1114   {
1115     EL_GAME_OF_LIFE,                    -1,
1116     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1117     &li.game_of_life[2],                3
1118   },
1119   {
1120     EL_GAME_OF_LIFE,                    -1,
1121     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1122     &li.game_of_life[3],                3
1123   },
1124   {
1125     EL_GAME_OF_LIFE,                    -1,
1126     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(5),
1127     &li.use_life_bugs,                  FALSE
1128   },
1129
1130   {
1131     EL_BIOMAZE,                         -1,
1132     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1133     &li.biomaze[0],                     2
1134   },
1135   {
1136     EL_BIOMAZE,                         -1,
1137     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1138     &li.biomaze[1],                     3
1139   },
1140   {
1141     EL_BIOMAZE,                         -1,
1142     TYPE_INTEGER,                       CONF_VALUE_8_BIT(3),
1143     &li.biomaze[2],                     3
1144   },
1145   {
1146     EL_BIOMAZE,                         -1,
1147     TYPE_INTEGER,                       CONF_VALUE_8_BIT(4),
1148     &li.biomaze[3],                     3
1149   },
1150
1151   {
1152     EL_TIMEGATE_SWITCH,                 -1,
1153     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1154     &li.time_timegate,                  10
1155   },
1156
1157   {
1158     EL_LIGHT_SWITCH_ACTIVE,             -1,
1159     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1160     &li.time_light,                     10
1161   },
1162
1163   {
1164     EL_SHIELD_NORMAL,                   -1,
1165     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1166     &li.shield_normal_time,             10
1167   },
1168   {
1169     EL_SHIELD_NORMAL,                   -1,
1170     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1171     &li.score[SC_SHIELD],               10
1172   },
1173
1174   {
1175     EL_SHIELD_DEADLY,                   -1,
1176     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1177     &li.shield_deadly_time,             10
1178   },
1179   {
1180     EL_SHIELD_DEADLY,                   -1,
1181     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1182     &li.score[SC_SHIELD],               10
1183   },
1184
1185   {
1186     EL_EXTRA_TIME,                      -1,
1187     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1188     &li.extra_time,                     10
1189   },
1190   {
1191     EL_EXTRA_TIME,                      -1,
1192     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1193     &li.extra_time_score,               10
1194   },
1195
1196   {
1197     EL_TIME_ORB_FULL,                   -1,
1198     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1199     &li.time_orb_time,                  10
1200   },
1201   {
1202     EL_TIME_ORB_FULL,                   -1,
1203     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1204     &li.use_time_orb_bug,               FALSE
1205   },
1206
1207   {
1208     EL_SPRING,                          -1,
1209     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1210     &li.use_spring_bug,                 FALSE
1211   },
1212
1213   {
1214     EL_EMC_ANDROID,                     -1,
1215     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1216     &li.android_move_time,              10
1217   },
1218   {
1219     EL_EMC_ANDROID,                     -1,
1220     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1221     &li.android_clone_time,             10
1222   },
1223   {
1224     EL_EMC_ANDROID,                     SAVE_CONF_NEVER,
1225     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1226     &li.android_clone_element[0],       EL_EMPTY, NULL,
1227     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS_OLD
1228   },
1229   {
1230     EL_EMC_ANDROID,                     -1,
1231     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1232     &li.android_clone_element[0],       EL_EMPTY, NULL,
1233     &li.num_android_clone_elements,     1, MAX_ANDROID_ELEMENTS
1234   },
1235
1236   {
1237     EL_EMC_LENSES,                      -1,
1238     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1239     &li.lenses_score,                   10
1240   },
1241   {
1242     EL_EMC_LENSES,                      -1,
1243     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1244     &li.lenses_time,                    10
1245   },
1246
1247   {
1248     EL_EMC_MAGNIFIER,                   -1,
1249     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1250     &li.magnify_score,                  10
1251   },
1252   {
1253     EL_EMC_MAGNIFIER,                   -1,
1254     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1255     &li.magnify_time,                   10
1256   },
1257
1258   {
1259     EL_EMC_MAGIC_BALL,                  -1,
1260     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1261     &li.ball_time,                      10
1262   },
1263   {
1264     EL_EMC_MAGIC_BALL,                  -1,
1265     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1266     &li.ball_random,                    FALSE
1267   },
1268   {
1269     EL_EMC_MAGIC_BALL,                  -1,
1270     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1271     &li.ball_active_initial,            FALSE
1272   },
1273   {
1274     EL_EMC_MAGIC_BALL,                  -1,
1275     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1276     &li.ball_content,                   EL_EMPTY, NULL,
1277     &li.num_ball_contents,              4, MAX_ELEMENT_CONTENTS
1278   },
1279
1280   {
1281     EL_SOKOBAN_FIELD_EMPTY,             -1,
1282     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1283     &li.sb_fields_needed,               TRUE
1284   },
1285
1286   {
1287     EL_SOKOBAN_OBJECT,                  -1,
1288     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1289     &li.sb_objects_needed,              TRUE
1290   },
1291
1292   {
1293     EL_MM_MCDUFFIN,                     -1,
1294     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1295     &li.mm_laser_red,                   FALSE
1296   },
1297   {
1298     EL_MM_MCDUFFIN,                     -1,
1299     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1300     &li.mm_laser_green,                 FALSE
1301   },
1302   {
1303     EL_MM_MCDUFFIN,                     -1,
1304     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1305     &li.mm_laser_blue,                  TRUE
1306   },
1307
1308   {
1309     EL_DF_LASER,                        -1,
1310     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1311     &li.df_laser_red,                   TRUE
1312   },
1313   {
1314     EL_DF_LASER,                        -1,
1315     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1316     &li.df_laser_green,                 TRUE
1317   },
1318   {
1319     EL_DF_LASER,                        -1,
1320     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1321     &li.df_laser_blue,                  FALSE
1322   },
1323
1324   {
1325     EL_MM_FUSE_ACTIVE,                  -1,
1326     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1327     &li.mm_time_fuse,                   25
1328   },
1329   {
1330     EL_MM_BOMB,                         -1,
1331     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1332     &li.mm_time_bomb,                   75
1333   },
1334
1335   {
1336     EL_MM_GRAY_BALL,                    -1,
1337     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1338     &li.mm_time_ball,                   75
1339   },
1340   {
1341     EL_MM_GRAY_BALL,                    -1,
1342     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1343     &li.mm_ball_choice_mode,            ANIM_RANDOM
1344   },
1345   {
1346     EL_MM_GRAY_BALL,                    -1,
1347     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(1),
1348     &li.mm_ball_content,                EL_EMPTY, NULL,
1349     &li.num_mm_ball_contents,           8, MAX_MM_BALL_CONTENTS
1350   },
1351   {
1352     EL_MM_GRAY_BALL,                    -1,
1353     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1354     &li.rotate_mm_ball_content,         TRUE
1355   },
1356   {
1357     EL_MM_GRAY_BALL,                    -1,
1358     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1359     &li.explode_mm_ball,                FALSE
1360   },
1361
1362   {
1363     EL_MM_STEEL_BLOCK,                  -1,
1364     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1365     &li.mm_time_block,                  75
1366   },
1367   {
1368     EL_MM_LIGHTBALL,                    -1,
1369     TYPE_INTEGER,                       CONF_VALUE_16_BIT(1),
1370     &li.score[SC_ELEM_BONUS],           10
1371   },
1372
1373   {
1374     -1,                                 -1,
1375     -1,                                 -1,
1376     NULL,                               -1
1377   }
1378 };
1379
1380 static struct LevelFileConfigInfo chunk_config_NOTE[] =
1381 {
1382   {
1383     -1,                                 -1,
1384     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1385     &xx_envelope.xsize,                 MAX_ENVELOPE_XSIZE,
1386   },
1387   {
1388     -1,                                 -1,
1389     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1390     &xx_envelope.ysize,                 MAX_ENVELOPE_YSIZE,
1391   },
1392
1393   {
1394     -1,                                 -1,
1395     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1396     &xx_envelope.autowrap,              FALSE
1397   },
1398   {
1399     -1,                                 -1,
1400     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(4),
1401     &xx_envelope.centered,              FALSE
1402   },
1403
1404   {
1405     -1,                                 -1,
1406     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1407     &xx_envelope.text,                  -1, NULL,
1408     &xx_string_length_unused,           -1, MAX_ENVELOPE_TEXT_LEN,
1409     &xx_default_string_empty[0]
1410   },
1411
1412   {
1413     -1,                                 -1,
1414     -1,                                 -1,
1415     NULL,                               -1
1416   }
1417 };
1418
1419 static struct LevelFileConfigInfo chunk_config_CUSX_base[] =
1420 {
1421   {
1422     -1,                                 -1,
1423     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1424     &xx_ei.description[0],              -1,
1425     &yy_ei.description[0],
1426     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1427     &xx_default_description[0]
1428   },
1429
1430   {
1431     -1,                                 -1,
1432     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1433     &xx_ei.properties[EP_BITFIELD_BASE_NR], EP_BITMASK_BASE_DEFAULT,
1434     &yy_ei.properties[EP_BITFIELD_BASE_NR]
1435   },
1436 #if ENABLE_RESERVED_CODE
1437   // (reserved for later use)
1438   {
1439     -1,                                 -1,
1440     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1441     &xx_ei.properties[EP_BITFIELD_BASE_NR + 1], EP_BITMASK_DEFAULT,
1442     &yy_ei.properties[EP_BITFIELD_BASE_NR + 1]
1443   },
1444 #endif
1445
1446   {
1447     -1,                                 -1,
1448     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1449     &xx_ei.use_gfx_element,             FALSE,
1450     &yy_ei.use_gfx_element
1451   },
1452   {
1453     -1,                                 -1,
1454     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1455     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE,
1456     &yy_ei.gfx_element_initial
1457   },
1458
1459   {
1460     -1,                                 -1,
1461     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(2),
1462     &xx_ei.access_direction,            MV_ALL_DIRECTIONS,
1463     &yy_ei.access_direction
1464   },
1465
1466   {
1467     -1,                                 -1,
1468     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1469     &xx_ei.collect_score_initial,       10,
1470     &yy_ei.collect_score_initial
1471   },
1472   {
1473     -1,                                 -1,
1474     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1475     &xx_ei.collect_count_initial,       1,
1476     &yy_ei.collect_count_initial
1477   },
1478
1479   {
1480     -1,                                 -1,
1481     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1482     &xx_ei.ce_value_fixed_initial,      0,
1483     &yy_ei.ce_value_fixed_initial
1484   },
1485   {
1486     -1,                                 -1,
1487     TYPE_INTEGER,                       CONF_VALUE_16_BIT(5),
1488     &xx_ei.ce_value_random_initial,     0,
1489     &yy_ei.ce_value_random_initial
1490   },
1491   {
1492     -1,                                 -1,
1493     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(3),
1494     &xx_ei.use_last_ce_value,           FALSE,
1495     &yy_ei.use_last_ce_value
1496   },
1497
1498   {
1499     -1,                                 -1,
1500     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1501     &xx_ei.push_delay_fixed,            8,
1502     &yy_ei.push_delay_fixed
1503   },
1504   {
1505     -1,                                 -1,
1506     TYPE_INTEGER,                       CONF_VALUE_16_BIT(7),
1507     &xx_ei.push_delay_random,           8,
1508     &yy_ei.push_delay_random
1509   },
1510   {
1511     -1,                                 -1,
1512     TYPE_INTEGER,                       CONF_VALUE_16_BIT(8),
1513     &xx_ei.drop_delay_fixed,            0,
1514     &yy_ei.drop_delay_fixed
1515   },
1516   {
1517     -1,                                 -1,
1518     TYPE_INTEGER,                       CONF_VALUE_16_BIT(9),
1519     &xx_ei.drop_delay_random,           0,
1520     &yy_ei.drop_delay_random
1521   },
1522   {
1523     -1,                                 -1,
1524     TYPE_INTEGER,                       CONF_VALUE_16_BIT(10),
1525     &xx_ei.move_delay_fixed,            0,
1526     &yy_ei.move_delay_fixed
1527   },
1528   {
1529     -1,                                 -1,
1530     TYPE_INTEGER,                       CONF_VALUE_16_BIT(11),
1531     &xx_ei.move_delay_random,           0,
1532     &yy_ei.move_delay_random
1533   },
1534   {
1535     -1,                                 -1,
1536     TYPE_INTEGER,                       CONF_VALUE_16_BIT(16),
1537     &xx_ei.step_delay_fixed,            0,
1538     &yy_ei.step_delay_fixed
1539   },
1540   {
1541     -1,                                 -1,
1542     TYPE_INTEGER,                       CONF_VALUE_16_BIT(17),
1543     &xx_ei.step_delay_random,           0,
1544     &yy_ei.step_delay_random
1545   },
1546
1547   {
1548     -1,                                 -1,
1549     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1550     &xx_ei.move_pattern,                MV_ALL_DIRECTIONS,
1551     &yy_ei.move_pattern
1552   },
1553   {
1554     -1,                                 -1,
1555     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1556     &xx_ei.move_direction_initial,      MV_START_AUTOMATIC,
1557     &yy_ei.move_direction_initial
1558   },
1559   {
1560     -1,                                 -1,
1561     TYPE_INTEGER,                       CONF_VALUE_8_BIT(5),
1562     &xx_ei.move_stepsize,               TILEX / 8,
1563     &yy_ei.move_stepsize
1564   },
1565
1566   {
1567     -1,                                 -1,
1568     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(12),
1569     &xx_ei.move_enter_element,          EL_EMPTY_SPACE,
1570     &yy_ei.move_enter_element
1571   },
1572   {
1573     -1,                                 -1,
1574     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(13),
1575     &xx_ei.move_leave_element,          EL_EMPTY_SPACE,
1576     &yy_ei.move_leave_element
1577   },
1578   {
1579     -1,                                 -1,
1580     TYPE_INTEGER,                       CONF_VALUE_8_BIT(6),
1581     &xx_ei.move_leave_type,             LEAVE_TYPE_UNLIMITED,
1582     &yy_ei.move_leave_type
1583   },
1584
1585   {
1586     -1,                                 -1,
1587     TYPE_INTEGER,                       CONF_VALUE_8_BIT(7),
1588     &xx_ei.slippery_type,               SLIPPERY_ANY_RANDOM,
1589     &yy_ei.slippery_type
1590   },
1591
1592   {
1593     -1,                                 -1,
1594     TYPE_INTEGER,                       CONF_VALUE_8_BIT(8),
1595     &xx_ei.explosion_type,              EXPLODES_3X3,
1596     &yy_ei.explosion_type
1597   },
1598   {
1599     -1,                                 -1,
1600     TYPE_INTEGER,                       CONF_VALUE_16_BIT(14),
1601     &xx_ei.explosion_delay,             16,
1602     &yy_ei.explosion_delay
1603   },
1604   {
1605     -1,                                 -1,
1606     TYPE_INTEGER,                       CONF_VALUE_16_BIT(15),
1607     &xx_ei.ignition_delay,              8,
1608     &yy_ei.ignition_delay
1609   },
1610
1611   {
1612     -1,                                 -1,
1613     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(2),
1614     &xx_ei.content,                     EL_EMPTY_SPACE,
1615     &yy_ei.content,
1616     &xx_num_contents,                   1, 1
1617   },
1618
1619   // ---------- "num_change_pages" must be the last entry ---------------------
1620
1621   {
1622     -1,                                 SAVE_CONF_ALWAYS,
1623     TYPE_INTEGER,                       CONF_VALUE_8_BIT(9),
1624     &xx_ei.num_change_pages,            1,
1625     &yy_ei.num_change_pages
1626   },
1627
1628   {
1629     -1,                                 -1,
1630     -1,                                 -1,
1631     NULL,                               -1,
1632     NULL
1633   }
1634 };
1635
1636 static struct LevelFileConfigInfo chunk_config_CUSX_change[] =
1637 {
1638   // ---------- "current_change_page" must be the first entry -----------------
1639
1640   {
1641     -1,                                 SAVE_CONF_ALWAYS,
1642     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1643     &xx_current_change_page,            -1
1644   },
1645
1646   // ---------- (the remaining entries can be in any order) -------------------
1647
1648   {
1649     -1,                                 -1,
1650     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(2),
1651     &xx_change.can_change,              FALSE
1652   },
1653
1654   {
1655     -1,                                 -1,
1656     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(1),
1657     &xx_event_bits[0],                  0
1658   },
1659   {
1660     -1,                                 -1,
1661     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(2),
1662     &xx_event_bits[1],                  0
1663   },
1664
1665   {
1666     -1,                                 -1,
1667     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(3),
1668     &xx_change.trigger_player,          CH_PLAYER_ANY
1669   },
1670   {
1671     -1,                                 -1,
1672     TYPE_BITFIELD,                      CONF_VALUE_8_BIT(4),
1673     &xx_change.trigger_side,            CH_SIDE_ANY
1674   },
1675   {
1676     -1,                                 -1,
1677     TYPE_BITFIELD,                      CONF_VALUE_32_BIT(3),
1678     &xx_change.trigger_page,            CH_PAGE_ANY
1679   },
1680
1681   {
1682     -1,                                 -1,
1683     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1684     &xx_change.target_element,          EL_EMPTY_SPACE
1685   },
1686
1687   {
1688     -1,                                 -1,
1689     TYPE_INTEGER,                       CONF_VALUE_16_BIT(2),
1690     &xx_change.delay_fixed,             0
1691   },
1692   {
1693     -1,                                 -1,
1694     TYPE_INTEGER,                       CONF_VALUE_16_BIT(3),
1695     &xx_change.delay_random,            0
1696   },
1697   {
1698     -1,                                 -1,
1699     TYPE_INTEGER,                       CONF_VALUE_16_BIT(4),
1700     &xx_change.delay_frames,            FRAMES_PER_SECOND
1701   },
1702
1703   {
1704     -1,                                 -1,
1705     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(5),
1706     &xx_change.initial_trigger_element, EL_EMPTY_SPACE
1707   },
1708
1709   {
1710     -1,                                 -1,
1711     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(6),
1712     &xx_change.explode,                 FALSE
1713   },
1714   {
1715     -1,                                 -1,
1716     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(7),
1717     &xx_change.use_target_content,      FALSE
1718   },
1719   {
1720     -1,                                 -1,
1721     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(8),
1722     &xx_change.only_if_complete,        FALSE
1723   },
1724   {
1725     -1,                                 -1,
1726     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1727     &xx_change.use_random_replace,      FALSE
1728   },
1729   {
1730     -1,                                 -1,
1731     TYPE_INTEGER,                       CONF_VALUE_8_BIT(10),
1732     &xx_change.random_percentage,       100
1733   },
1734   {
1735     -1,                                 -1,
1736     TYPE_INTEGER,                       CONF_VALUE_8_BIT(11),
1737     &xx_change.replace_when,            CP_WHEN_EMPTY
1738   },
1739
1740   {
1741     -1,                                 -1,
1742     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1743     &xx_change.has_action,              FALSE
1744   },
1745   {
1746     -1,                                 -1,
1747     TYPE_INTEGER,                       CONF_VALUE_8_BIT(13),
1748     &xx_change.action_type,             CA_NO_ACTION
1749   },
1750   {
1751     -1,                                 -1,
1752     TYPE_INTEGER,                       CONF_VALUE_8_BIT(14),
1753     &xx_change.action_mode,             CA_MODE_UNDEFINED
1754   },
1755   {
1756     -1,                                 -1,
1757     TYPE_INTEGER,                       CONF_VALUE_16_BIT(6),
1758     &xx_change.action_arg,              CA_ARG_UNDEFINED
1759   },
1760
1761   {
1762     -1,                                 -1,
1763     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(7),
1764     &xx_change.action_element,          EL_EMPTY_SPACE
1765   },
1766
1767   {
1768     -1,                                 -1,
1769     TYPE_CONTENT_LIST,                  CONF_VALUE_BYTES(1),
1770     &xx_change.target_content,          EL_EMPTY_SPACE, NULL,
1771     &xx_num_contents,                   1, 1
1772   },
1773
1774   {
1775     -1,                                 -1,
1776     -1,                                 -1,
1777     NULL,                               -1
1778   }
1779 };
1780
1781 static struct LevelFileConfigInfo chunk_config_GRPX[] =
1782 {
1783   {
1784     -1,                                 -1,
1785     TYPE_STRING,                        CONF_VALUE_BYTES(1),
1786     &xx_ei.description[0],              -1, NULL,
1787     &xx_string_length_unused,           -1, MAX_ELEMENT_NAME_LEN,
1788     &xx_default_description[0]
1789   },
1790
1791   {
1792     -1,                                 -1,
1793     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1794     &xx_ei.use_gfx_element,             FALSE
1795   },
1796   {
1797     -1,                                 -1,
1798     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1799     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1800   },
1801
1802   {
1803     -1,                                 -1,
1804     TYPE_INTEGER,                       CONF_VALUE_8_BIT(2),
1805     &xx_group.choice_mode,              ANIM_RANDOM
1806   },
1807
1808   {
1809     -1,                                 -1,
1810     TYPE_ELEMENT_LIST,                  CONF_VALUE_BYTES(2),
1811     &xx_group.element[0],               EL_EMPTY_SPACE, NULL,
1812     &xx_group.num_elements,             1, MAX_ELEMENTS_IN_GROUP
1813   },
1814
1815   {
1816     -1,                                 -1,
1817     -1,                                 -1,
1818     NULL,                               -1
1819   }
1820 };
1821
1822 static struct LevelFileConfigInfo chunk_config_EMPX[] =
1823 {
1824   {
1825     -1,                                 -1,
1826     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(1),
1827     &xx_ei.use_gfx_element,             FALSE
1828   },
1829   {
1830     -1,                                 -1,
1831     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1832     &xx_ei.gfx_element_initial,         EL_EMPTY_SPACE
1833   },
1834
1835   {
1836     -1,                                 -1,
1837     -1,                                 -1,
1838     NULL,                               -1
1839   }
1840 };
1841
1842 static struct LevelFileConfigInfo chunk_config_CONF[] =         // (OBSOLETE)
1843 {
1844   {
1845     EL_PLAYER_1,                        -1,
1846     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(9),
1847     &li.block_snap_field,               TRUE
1848   },
1849   {
1850     EL_PLAYER_1,                        -1,
1851     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(13),
1852     &li.continuous_snapping,            TRUE
1853   },
1854   {
1855     EL_PLAYER_1,                        -1,
1856     TYPE_INTEGER,                       CONF_VALUE_8_BIT(1),
1857     &li.initial_player_stepsize[0],     STEPSIZE_NORMAL
1858   },
1859   {
1860     EL_PLAYER_1,                        -1,
1861     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(10),
1862     &li.use_start_element[0],           FALSE
1863   },
1864   {
1865     EL_PLAYER_1,                        -1,
1866     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(1),
1867     &li.start_element[0],               EL_PLAYER_1
1868   },
1869   {
1870     EL_PLAYER_1,                        -1,
1871     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(11),
1872     &li.use_artwork_element[0],         FALSE
1873   },
1874   {
1875     EL_PLAYER_1,                        -1,
1876     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(2),
1877     &li.artwork_element[0],             EL_PLAYER_1
1878   },
1879   {
1880     EL_PLAYER_1,                        -1,
1881     TYPE_BOOLEAN,                       CONF_VALUE_8_BIT(12),
1882     &li.use_explosion_element[0],       FALSE
1883   },
1884   {
1885     EL_PLAYER_1,                        -1,
1886     TYPE_ELEMENT,                       CONF_VALUE_16_BIT(3),
1887     &li.explosion_element[0],           EL_PLAYER_1
1888   },
1889
1890   {
1891     -1,                                 -1,
1892     -1,                                 -1,
1893     NULL,                               -1
1894   }
1895 };
1896
1897 static struct
1898 {
1899   int filetype;
1900   char *id;
1901 }
1902 filetype_id_list[] =
1903 {
1904   { LEVEL_FILE_TYPE_RND,        "RND"   },
1905   { LEVEL_FILE_TYPE_BD,         "BD"    },
1906   { LEVEL_FILE_TYPE_EM,         "EM"    },
1907   { LEVEL_FILE_TYPE_SP,         "SP"    },
1908   { LEVEL_FILE_TYPE_DX,         "DX"    },
1909   { LEVEL_FILE_TYPE_SB,         "SB"    },
1910   { LEVEL_FILE_TYPE_DC,         "DC"    },
1911   { LEVEL_FILE_TYPE_MM,         "MM"    },
1912   { LEVEL_FILE_TYPE_MM,         "DF"    },
1913   { -1,                         NULL    },
1914 };
1915
1916
1917 // ============================================================================
1918 // level file functions
1919 // ============================================================================
1920
1921 static boolean check_special_flags(char *flag)
1922 {
1923   if (strEqual(options.special_flags, flag) ||
1924       strEqual(leveldir_current->special_flags, flag))
1925     return TRUE;
1926
1927   return FALSE;
1928 }
1929
1930 static struct DateInfo getCurrentDate(void)
1931 {
1932   time_t epoch_seconds = time(NULL);
1933   struct tm *now = localtime(&epoch_seconds);
1934   struct DateInfo date;
1935
1936   date.year  = now->tm_year + 1900;
1937   date.month = now->tm_mon  + 1;
1938   date.day   = now->tm_mday;
1939
1940   date.src   = DATE_SRC_CLOCK;
1941
1942   return date;
1943 }
1944
1945 static void resetEventFlags(struct ElementChangeInfo *change)
1946 {
1947   int i;
1948
1949   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1950     change->has_event[i] = FALSE;
1951 }
1952
1953 static void resetEventBits(void)
1954 {
1955   int i;
1956
1957   for (i = 0; i < NUM_CE_BITFIELDS; i++)
1958     xx_event_bits[i] = 0;
1959 }
1960
1961 static void setEventFlagsFromEventBits(struct ElementChangeInfo *change)
1962 {
1963   int i;
1964
1965   /* important: only change event flag if corresponding event bit is set
1966      (this is because all xx_event_bits[] values are loaded separately,
1967      and all xx_event_bits[] values are set back to zero before loading
1968      another value xx_event_bits[x] (each value representing 32 flags)) */
1969
1970   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1971     if (xx_event_bits[CH_EVENT_BITFIELD_NR(i)] & CH_EVENT_BIT(i))
1972       change->has_event[i] = TRUE;
1973 }
1974
1975 static void setEventBitsFromEventFlags(struct ElementChangeInfo *change)
1976 {
1977   int i;
1978
1979   /* in contrast to the above function setEventFlagsFromEventBits(), it
1980      would also be possible to set all bits in xx_event_bits[] to 0 or 1
1981      depending on the corresponding change->has_event[i] values here, as
1982      all xx_event_bits[] values are reset in resetEventBits() before */
1983
1984   for (i = 0; i < NUM_CHANGE_EVENTS; i++)
1985     if (change->has_event[i])
1986       xx_event_bits[CH_EVENT_BITFIELD_NR(i)] |= CH_EVENT_BIT(i);
1987 }
1988
1989 static char *getDefaultElementDescription(struct ElementInfo *ei)
1990 {
1991   static char description[MAX_ELEMENT_NAME_LEN + 1];
1992   char *default_description = (ei->custom_description != NULL ?
1993                                ei->custom_description :
1994                                ei->editor_description);
1995   int i;
1996
1997   // always start with reliable default values
1998   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
1999     description[i] = '\0';
2000
2001   // truncate element description to MAX_ELEMENT_NAME_LEN bytes
2002   strncpy(description, default_description, MAX_ELEMENT_NAME_LEN);
2003
2004   return &description[0];
2005 }
2006
2007 static void setElementDescriptionToDefault(struct ElementInfo *ei)
2008 {
2009   char *default_description = getDefaultElementDescription(ei);
2010   int i;
2011
2012   for (i = 0; i < MAX_ELEMENT_NAME_LEN + 1; i++)
2013     ei->description[i] = default_description[i];
2014 }
2015
2016 static void setConfigToDefaultsFromConfigList(struct LevelFileConfigInfo *conf)
2017 {
2018   int i;
2019
2020   for (i = 0; conf[i].data_type != -1; i++)
2021   {
2022     int default_value = conf[i].default_value;
2023     int data_type = conf[i].data_type;
2024     int conf_type = conf[i].conf_type;
2025     int byte_mask = conf_type & CONF_MASK_BYTES;
2026
2027     if (byte_mask == CONF_MASK_MULTI_BYTES)
2028     {
2029       int default_num_entities = conf[i].default_num_entities;
2030       int max_num_entities = conf[i].max_num_entities;
2031
2032       *(int *)(conf[i].num_entities) = default_num_entities;
2033
2034       if (data_type == TYPE_STRING)
2035       {
2036         char *default_string = conf[i].default_string;
2037         char *string = (char *)(conf[i].value);
2038
2039         strncpy(string, default_string, max_num_entities);
2040       }
2041       else if (data_type == TYPE_ELEMENT_LIST)
2042       {
2043         int *element_array = (int *)(conf[i].value);
2044         int j;
2045
2046         for (j = 0; j < max_num_entities; j++)
2047           element_array[j] = default_value;
2048       }
2049       else if (data_type == TYPE_CONTENT_LIST)
2050       {
2051         struct Content *content = (struct Content *)(conf[i].value);
2052         int c, x, y;
2053
2054         for (c = 0; c < max_num_entities; c++)
2055           for (y = 0; y < 3; y++)
2056             for (x = 0; x < 3; x++)
2057               content[c].e[x][y] = default_value;
2058       }
2059     }
2060     else        // constant size configuration data (1, 2 or 4 bytes)
2061     {
2062       if (data_type == TYPE_BOOLEAN)
2063         *(boolean *)(conf[i].value) = default_value;
2064       else
2065         *(int *)    (conf[i].value) = default_value;
2066     }
2067   }
2068 }
2069
2070 static void copyConfigFromConfigList(struct LevelFileConfigInfo *conf)
2071 {
2072   int i;
2073
2074   for (i = 0; conf[i].data_type != -1; i++)
2075   {
2076     int data_type = conf[i].data_type;
2077     int conf_type = conf[i].conf_type;
2078     int byte_mask = conf_type & CONF_MASK_BYTES;
2079
2080     if (byte_mask == CONF_MASK_MULTI_BYTES)
2081     {
2082       int max_num_entities = conf[i].max_num_entities;
2083
2084       if (data_type == TYPE_STRING)
2085       {
2086         char *string      = (char *)(conf[i].value);
2087         char *string_copy = (char *)(conf[i].value_copy);
2088
2089         strncpy(string_copy, string, max_num_entities);
2090       }
2091       else if (data_type == TYPE_ELEMENT_LIST)
2092       {
2093         int *element_array      = (int *)(conf[i].value);
2094         int *element_array_copy = (int *)(conf[i].value_copy);
2095         int j;
2096
2097         for (j = 0; j < max_num_entities; j++)
2098           element_array_copy[j] = element_array[j];
2099       }
2100       else if (data_type == TYPE_CONTENT_LIST)
2101       {
2102         struct Content *content      = (struct Content *)(conf[i].value);
2103         struct Content *content_copy = (struct Content *)(conf[i].value_copy);
2104         int c, x, y;
2105
2106         for (c = 0; c < max_num_entities; c++)
2107           for (y = 0; y < 3; y++)
2108             for (x = 0; x < 3; x++)
2109               content_copy[c].e[x][y] = content[c].e[x][y];
2110       }
2111     }
2112     else        // constant size configuration data (1, 2 or 4 bytes)
2113     {
2114       if (data_type == TYPE_BOOLEAN)
2115         *(boolean *)(conf[i].value_copy) = *(boolean *)(conf[i].value);
2116       else
2117         *(int *)    (conf[i].value_copy) = *(int *)    (conf[i].value);
2118     }
2119   }
2120 }
2121
2122 void copyElementInfo(struct ElementInfo *ei_from, struct ElementInfo *ei_to)
2123 {
2124   int i;
2125
2126   xx_ei = *ei_from;     // copy element data into temporary buffer
2127   yy_ei = *ei_to;       // copy element data into temporary buffer
2128
2129   copyConfigFromConfigList(chunk_config_CUSX_base);
2130
2131   *ei_from = xx_ei;
2132   *ei_to   = yy_ei;
2133
2134   // ---------- reinitialize and copy change pages ----------
2135
2136   ei_to->num_change_pages = ei_from->num_change_pages;
2137   ei_to->current_change_page = ei_from->current_change_page;
2138
2139   setElementChangePages(ei_to, ei_to->num_change_pages);
2140
2141   for (i = 0; i < ei_to->num_change_pages; i++)
2142     ei_to->change_page[i] = ei_from->change_page[i];
2143
2144   // ---------- copy group element info ----------
2145   if (ei_from->group != NULL && ei_to->group != NULL)   // group or internal
2146     *ei_to->group = *ei_from->group;
2147
2148   // mark this custom element as modified
2149   ei_to->modified_settings = TRUE;
2150 }
2151
2152 void setElementChangePages(struct ElementInfo *ei, int change_pages)
2153 {
2154   int change_page_size = sizeof(struct ElementChangeInfo);
2155
2156   ei->num_change_pages = MAX(1, change_pages);
2157
2158   ei->change_page =
2159     checked_realloc(ei->change_page, ei->num_change_pages * change_page_size);
2160
2161   if (ei->current_change_page >= ei->num_change_pages)
2162     ei->current_change_page = ei->num_change_pages - 1;
2163
2164   ei->change = &ei->change_page[ei->current_change_page];
2165 }
2166
2167 void setElementChangeInfoToDefaults(struct ElementChangeInfo *change)
2168 {
2169   xx_change = *change;          // copy change data into temporary buffer
2170
2171   setConfigToDefaultsFromConfigList(chunk_config_CUSX_change);
2172
2173   *change = xx_change;
2174
2175   resetEventFlags(change);
2176
2177   change->direct_action = 0;
2178   change->other_action = 0;
2179
2180   change->pre_change_function = NULL;
2181   change->change_function = NULL;
2182   change->post_change_function = NULL;
2183 }
2184
2185 static void setLevelInfoToDefaults_Level(struct LevelInfo *level)
2186 {
2187   int i, x, y;
2188
2189   li = *level;          // copy level data into temporary buffer
2190   setConfigToDefaultsFromConfigList(chunk_config_INFO);
2191   *level = li;          // copy temporary buffer back to level data
2192
2193   setLevelInfoToDefaults_BD();
2194   setLevelInfoToDefaults_EM();
2195   setLevelInfoToDefaults_SP();
2196   setLevelInfoToDefaults_MM();
2197
2198   level->native_bd_level = &native_bd_level;
2199   level->native_em_level = &native_em_level;
2200   level->native_sp_level = &native_sp_level;
2201   level->native_mm_level = &native_mm_level;
2202
2203   level->file_version = FILE_VERSION_ACTUAL;
2204   level->game_version = GAME_VERSION_ACTUAL;
2205
2206   level->creation_date = getCurrentDate();
2207
2208   level->encoding_16bit_field  = TRUE;
2209   level->encoding_16bit_yamyam = TRUE;
2210   level->encoding_16bit_amoeba = TRUE;
2211
2212   // clear level name and level author string buffers
2213   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2214     level->name[i] = '\0';
2215   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
2216     level->author[i] = '\0';
2217
2218   // set level name and level author to default values
2219   strcpy(level->name, NAMELESS_LEVEL_NAME);
2220   strcpy(level->author, ANONYMOUS_NAME);
2221
2222   // set level playfield to playable default level with player and exit
2223   for (x = 0; x < MAX_LEV_FIELDX; x++)
2224     for (y = 0; y < MAX_LEV_FIELDY; y++)
2225       level->field[x][y] = EL_SAND;
2226
2227   level->field[0][0] = EL_PLAYER_1;
2228   level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
2229
2230   BorderElement = EL_STEELWALL;
2231
2232   // detect custom elements when loading them
2233   level->file_has_custom_elements = FALSE;
2234
2235   // set all bug compatibility flags to "false" => do not emulate this bug
2236   level->use_action_after_change_bug = FALSE;
2237
2238   if (leveldir_current)
2239   {
2240     // try to determine better author name than 'anonymous'
2241     if (!strEqual(leveldir_current->author, ANONYMOUS_NAME))
2242     {
2243       strncpy(level->author, leveldir_current->author, MAX_LEVEL_AUTHOR_LEN);
2244       level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2245     }
2246     else
2247     {
2248       switch (LEVELCLASS(leveldir_current))
2249       {
2250         case LEVELCLASS_TUTORIAL:
2251           strcpy(level->author, PROGRAM_AUTHOR_STRING);
2252           break;
2253
2254         case LEVELCLASS_CONTRIB:
2255           strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
2256           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2257           break;
2258
2259         case LEVELCLASS_PRIVATE:
2260           strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
2261           level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
2262           break;
2263
2264         default:
2265           // keep default value
2266           break;
2267       }
2268     }
2269   }
2270 }
2271
2272 static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
2273 {
2274   static boolean clipboard_elements_initialized = FALSE;
2275   int i;
2276
2277   InitElementPropertiesStatic();
2278
2279   li = *level;          // copy level data into temporary buffer
2280   setConfigToDefaultsFromConfigList(chunk_config_ELEM);
2281   *level = li;          // copy temporary buffer back to level data
2282
2283   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2284   {
2285     int element = i;
2286     struct ElementInfo *ei = &element_info[element];
2287
2288     if (element == EL_MM_GRAY_BALL)
2289     {
2290       struct LevelInfo_MM *level_mm = level->native_mm_level;
2291       int j;
2292
2293       for (j = 0; j < level->num_mm_ball_contents; j++)
2294         level->mm_ball_content[j] =
2295           map_element_MM_to_RND(level_mm->ball_content[j]);
2296     }
2297
2298     // never initialize clipboard elements after the very first time
2299     // (to be able to use clipboard elements between several levels)
2300     if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
2301       continue;
2302
2303     if (IS_ENVELOPE(element))
2304     {
2305       int envelope_nr = element - EL_ENVELOPE_1;
2306
2307       setConfigToDefaultsFromConfigList(chunk_config_NOTE);
2308
2309       level->envelope[envelope_nr] = xx_envelope;
2310     }
2311
2312     if (IS_CUSTOM_ELEMENT(element) ||
2313         IS_GROUP_ELEMENT(element) ||
2314         IS_INTERNAL_ELEMENT(element))
2315     {
2316       xx_ei = *ei;      // copy element data into temporary buffer
2317
2318       setConfigToDefaultsFromConfigList(chunk_config_CUSX_base);
2319
2320       *ei = xx_ei;
2321     }
2322
2323     setElementChangePages(ei, 1);
2324     setElementChangeInfoToDefaults(ei->change);
2325
2326     if (IS_CUSTOM_ELEMENT(element) ||
2327         IS_GROUP_ELEMENT(element))
2328     {
2329       setElementDescriptionToDefault(ei);
2330
2331       ei->modified_settings = FALSE;
2332     }
2333
2334     if (IS_CUSTOM_ELEMENT(element) ||
2335         IS_INTERNAL_ELEMENT(element))
2336     {
2337       // internal values used in level editor
2338
2339       ei->access_type = 0;
2340       ei->access_layer = 0;
2341       ei->access_protected = 0;
2342       ei->walk_to_action = 0;
2343       ei->smash_targets = 0;
2344       ei->deadliness = 0;
2345
2346       ei->can_explode_by_fire = FALSE;
2347       ei->can_explode_smashed = FALSE;
2348       ei->can_explode_impact = FALSE;
2349
2350       ei->current_change_page = 0;
2351     }
2352
2353     if (IS_GROUP_ELEMENT(element) ||
2354         IS_INTERNAL_ELEMENT(element))
2355     {
2356       struct ElementGroupInfo *group;
2357
2358       // initialize memory for list of elements in group
2359       if (ei->group == NULL)
2360         ei->group = checked_malloc(sizeof(struct ElementGroupInfo));
2361
2362       group = ei->group;
2363
2364       xx_group = *group;        // copy group data into temporary buffer
2365
2366       setConfigToDefaultsFromConfigList(chunk_config_GRPX);
2367
2368       *group = xx_group;
2369     }
2370
2371     if (IS_EMPTY_ELEMENT(element) ||
2372         IS_INTERNAL_ELEMENT(element))
2373     {
2374       xx_ei = *ei;              // copy element data into temporary buffer
2375
2376       setConfigToDefaultsFromConfigList(chunk_config_EMPX);
2377
2378       *ei = xx_ei;
2379     }
2380   }
2381
2382   clipboard_elements_initialized = TRUE;
2383 }
2384
2385 static void setLevelInfoToDefaults(struct LevelInfo *level,
2386                                    boolean level_info_only,
2387                                    boolean reset_file_status)
2388 {
2389   setLevelInfoToDefaults_Level(level);
2390
2391   if (!level_info_only)
2392     setLevelInfoToDefaults_Elements(level);
2393
2394   if (reset_file_status)
2395   {
2396     level->no_valid_file = FALSE;
2397     level->no_level_file = FALSE;
2398   }
2399
2400   level->changed = FALSE;
2401 }
2402
2403 static void setFileInfoToDefaults(struct LevelFileInfo *level_file_info)
2404 {
2405   level_file_info->nr = 0;
2406   level_file_info->type = LEVEL_FILE_TYPE_UNKNOWN;
2407   level_file_info->packed = FALSE;
2408
2409   setString(&level_file_info->basename, NULL);
2410   setString(&level_file_info->filename, NULL);
2411 }
2412
2413 int getMappedElement_SB(int, boolean);
2414
2415 static void ActivateLevelTemplate(void)
2416 {
2417   int x, y;
2418
2419   if (check_special_flags("load_xsb_to_ces"))
2420   {
2421     // fill smaller playfields with padding "beyond border wall" elements
2422     if (level.fieldx < level_template.fieldx ||
2423         level.fieldy < level_template.fieldy)
2424     {
2425       short field[level.fieldx][level.fieldy];
2426       int new_fieldx = MAX(level.fieldx, level_template.fieldx);
2427       int new_fieldy = MAX(level.fieldy, level_template.fieldy);
2428       int pos_fieldx = (new_fieldx - level.fieldx) / 2;
2429       int pos_fieldy = (new_fieldy - level.fieldy) / 2;
2430
2431       // copy old playfield (which is smaller than the visible area)
2432       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2433         field[x][y] = level.field[x][y];
2434
2435       // fill new, larger playfield with "beyond border wall" elements
2436       for (y = 0; y < new_fieldy; y++) for (x = 0; x < new_fieldx; x++)
2437         level.field[x][y] = getMappedElement_SB('_', TRUE);
2438
2439       // copy the old playfield to the middle of the new playfield
2440       for (y = 0; y < level.fieldy; y++) for (x = 0; x < level.fieldx; x++)
2441         level.field[pos_fieldx + x][pos_fieldy + y] = field[x][y];
2442
2443       level.fieldx = new_fieldx;
2444       level.fieldy = new_fieldy;
2445     }
2446   }
2447
2448   // Currently there is no special action needed to activate the template
2449   // data, because 'element_info' property settings overwrite the original
2450   // level data, while all other variables do not change.
2451
2452   // Exception: 'from_level_template' elements in the original level playfield
2453   // are overwritten with the corresponding elements at the same position in
2454   // playfield from the level template.
2455
2456   for (x = 0; x < level.fieldx; x++)
2457     for (y = 0; y < level.fieldy; y++)
2458       if (level.field[x][y] == EL_FROM_LEVEL_TEMPLATE)
2459         level.field[x][y] = level_template.field[x][y];
2460
2461   if (check_special_flags("load_xsb_to_ces"))
2462   {
2463     struct LevelInfo level_backup = level;
2464
2465     // overwrite all individual level settings from template level settings
2466     level = level_template;
2467
2468     // restore level file info
2469     level.file_info = level_backup.file_info;
2470
2471     // restore playfield size
2472     level.fieldx = level_backup.fieldx;
2473     level.fieldy = level_backup.fieldy;
2474
2475     // restore playfield content
2476     for (x = 0; x < level.fieldx; x++)
2477       for (y = 0; y < level.fieldy; y++)
2478         level.field[x][y] = level_backup.field[x][y];
2479
2480     // restore name and author from individual level
2481     strcpy(level.name,   level_backup.name);
2482     strcpy(level.author, level_backup.author);
2483
2484     // restore flag "use_custom_template"
2485     level.use_custom_template = level_backup.use_custom_template;
2486   }
2487 }
2488
2489 static boolean checkForPackageFromBasename_BD(char *basename)
2490 {
2491   // check for native BD level file extensions
2492   if (!strSuffixLower(basename, ".bd") &&
2493       !strSuffixLower(basename, ".bdr") &&
2494       !strSuffixLower(basename, ".brc") &&
2495       !strSuffixLower(basename, ".gds"))
2496     return FALSE;
2497
2498   // check for standard single-level BD files (like "001.bd")
2499   if (strSuffixLower(basename, ".bd") &&
2500       strlen(basename) == 6 &&
2501       basename[0] >= '0' && basename[0] <= '9' &&
2502       basename[1] >= '0' && basename[1] <= '9' &&
2503       basename[2] >= '0' && basename[2] <= '9')
2504     return FALSE;
2505
2506   // this is a level package in native BD file format
2507   return TRUE;
2508 }
2509
2510 static char *getLevelFilenameFromBasename(char *basename)
2511 {
2512   static char *filename = NULL;
2513
2514   checked_free(filename);
2515
2516   filename = getPath2(getCurrentLevelDir(), basename);
2517
2518   return filename;
2519 }
2520
2521 static int getFileTypeFromBasename(char *basename)
2522 {
2523   // !!! ALSO SEE COMMENT IN checkForPackageFromBasename() !!!
2524
2525   static char *filename = NULL;
2526   struct stat file_status;
2527
2528   // ---------- try to determine file type from filename ----------
2529
2530   // check for typical filename of a Supaplex level package file
2531   if (strlen(basename) == 10 && strPrefixLower(basename, "levels.d"))
2532     return LEVEL_FILE_TYPE_SP;
2533
2534   // check for typical filename of a Diamond Caves II level package file
2535   if (strSuffixLower(basename, ".dc") ||
2536       strSuffixLower(basename, ".dc2"))
2537     return LEVEL_FILE_TYPE_DC;
2538
2539   // check for typical filename of a Sokoban level package file
2540   if (strSuffixLower(basename, ".xsb") &&
2541       strchr(basename, '%') == NULL)
2542     return LEVEL_FILE_TYPE_SB;
2543
2544   // check for typical filename of a Boulder Dash (GDash) level package file
2545   if (checkForPackageFromBasename_BD(basename))
2546     return LEVEL_FILE_TYPE_BD;
2547
2548   // ---------- try to determine file type from filesize ----------
2549
2550   checked_free(filename);
2551   filename = getPath2(getCurrentLevelDir(), basename);
2552
2553   if (stat(filename, &file_status) == 0)
2554   {
2555     // check for typical filesize of a Supaplex level package file
2556     if (file_status.st_size == 170496)
2557       return LEVEL_FILE_TYPE_SP;
2558   }
2559
2560   return LEVEL_FILE_TYPE_UNKNOWN;
2561 }
2562
2563 static int getFileTypeFromMagicBytes(char *filename, int type)
2564 {
2565   File *file;
2566
2567   if ((file = openFile(filename, MODE_READ)))
2568   {
2569     char chunk_name[CHUNK_ID_LEN + 1];
2570
2571     getFileChunkBE(file, chunk_name, NULL);
2572
2573     if (strEqual(chunk_name, "MMII") ||
2574         strEqual(chunk_name, "MIRR"))
2575       type = LEVEL_FILE_TYPE_MM;
2576
2577     closeFile(file);
2578   }
2579
2580   return type;
2581 }
2582
2583 static boolean checkForPackageFromBasename(char *basename)
2584 {
2585   // !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
2586   // !!! SINGLE LEVELS (CURRENTLY ONLY DETECTS LEVEL PACKAGES         !!!
2587
2588   return (getFileTypeFromBasename(basename) != LEVEL_FILE_TYPE_UNKNOWN);
2589 }
2590
2591 static char *getSingleLevelBasenameExt(int nr, char *extension)
2592 {
2593   static char basename[MAX_FILENAME_LEN];
2594
2595   if (nr < 0)
2596     sprintf(basename, "%s", LEVELTEMPLATE_FILENAME);
2597   else
2598     sprintf(basename, "%03d.%s", nr, extension);
2599
2600   return basename;
2601 }
2602
2603 static char *getSingleLevelBasename(int nr)
2604 {
2605   return getSingleLevelBasenameExt(nr, LEVELFILE_EXTENSION);
2606 }
2607
2608 static char *getPackedLevelBasename(int type)
2609 {
2610   static char basename[MAX_FILENAME_LEN];
2611   char *directory = getCurrentLevelDir();
2612   Directory *dir;
2613   DirectoryEntry *dir_entry;
2614
2615   strcpy(basename, UNDEFINED_FILENAME);         // default: undefined file
2616
2617   if ((dir = openDirectory(directory)) == NULL)
2618   {
2619     Warn("cannot read current level directory '%s'", directory);
2620
2621     return basename;
2622   }
2623
2624   while ((dir_entry = readDirectory(dir)) != NULL)      // loop all entries
2625   {
2626     char *entry_basename = dir_entry->basename;
2627     int entry_type = getFileTypeFromBasename(entry_basename);
2628
2629     if (entry_type != LEVEL_FILE_TYPE_UNKNOWN)  // found valid level package
2630     {
2631       if (type == LEVEL_FILE_TYPE_UNKNOWN ||
2632           type == entry_type)
2633       {
2634         strcpy(basename, entry_basename);
2635
2636         break;
2637       }
2638     }
2639   }
2640
2641   closeDirectory(dir);
2642
2643   return basename;
2644 }
2645
2646 static char *getSingleLevelFilename(int nr)
2647 {
2648   return getLevelFilenameFromBasename(getSingleLevelBasename(nr));
2649 }
2650
2651 #if ENABLE_UNUSED_CODE
2652 static char *getPackedLevelFilename(int type)
2653 {
2654   return getLevelFilenameFromBasename(getPackedLevelBasename(type));
2655 }
2656 #endif
2657
2658 char *getDefaultLevelFilename(int nr)
2659 {
2660   return getSingleLevelFilename(nr);
2661 }
2662
2663 #if ENABLE_UNUSED_CODE
2664 static void setLevelFileInfo_SingleLevelFilename(struct LevelFileInfo *lfi,
2665                                                  int type)
2666 {
2667   lfi->type = type;
2668   lfi->packed = FALSE;
2669
2670   setString(&lfi->basename, getSingleLevelBasename(lfi->nr, lfi->type));
2671   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2672 }
2673 #endif
2674
2675 static void setLevelFileInfo_FormatLevelFilename(struct LevelFileInfo *lfi,
2676                                                  int type, char *format, ...)
2677 {
2678   static char basename[MAX_FILENAME_LEN];
2679   va_list ap;
2680
2681   va_start(ap, format);
2682   vsprintf(basename, format, ap);
2683   va_end(ap);
2684
2685   lfi->type = type;
2686   lfi->packed = FALSE;
2687
2688   setString(&lfi->basename, basename);
2689   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2690 }
2691
2692 static void setLevelFileInfo_PackedLevelFilename(struct LevelFileInfo *lfi,
2693                                                  int type)
2694 {
2695   lfi->type = type;
2696   lfi->packed = TRUE;
2697
2698   setString(&lfi->basename, getPackedLevelBasename(lfi->type));
2699   setString(&lfi->filename, getLevelFilenameFromBasename(lfi->basename));
2700 }
2701
2702 static int getFiletypeFromID(char *filetype_id)
2703 {
2704   char *filetype_id_lower;
2705   int filetype = LEVEL_FILE_TYPE_UNKNOWN;
2706   int i;
2707
2708   if (filetype_id == NULL)
2709     return LEVEL_FILE_TYPE_UNKNOWN;
2710
2711   filetype_id_lower = getStringToLower(filetype_id);
2712
2713   for (i = 0; filetype_id_list[i].id != NULL; i++)
2714   {
2715     char *id_lower = getStringToLower(filetype_id_list[i].id);
2716     
2717     if (strEqual(filetype_id_lower, id_lower))
2718       filetype = filetype_id_list[i].filetype;
2719
2720     free(id_lower);
2721
2722     if (filetype != LEVEL_FILE_TYPE_UNKNOWN)
2723       break;
2724   }
2725
2726   free(filetype_id_lower);
2727
2728   return filetype;
2729 }
2730
2731 char *getLocalLevelTemplateFilename(void)
2732 {
2733   return getDefaultLevelFilename(-1);
2734 }
2735
2736 char *getGlobalLevelTemplateFilename(void)
2737 {
2738   // global variable "leveldir_current" must be modified in the loop below
2739   LevelDirTree *leveldir_current_last = leveldir_current;
2740   char *filename = NULL;
2741
2742   // check for template level in path from current to topmost tree node
2743
2744   while (leveldir_current != NULL)
2745   {
2746     filename = getDefaultLevelFilename(-1);
2747
2748     if (fileExists(filename))
2749       break;
2750
2751     leveldir_current = leveldir_current->node_parent;
2752   }
2753
2754   // restore global variable "leveldir_current" modified in above loop
2755   leveldir_current = leveldir_current_last;
2756
2757   return filename;
2758 }
2759
2760 static void determineLevelFileInfo_Filename(struct LevelFileInfo *lfi)
2761 {
2762   int nr = lfi->nr;
2763
2764   // special case: level number is negative => check for level template file
2765   if (nr < 0)
2766   {
2767     setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2768                                          getSingleLevelBasename(-1));
2769
2770     // replace local level template filename with global template filename
2771     setString(&lfi->filename, getGlobalLevelTemplateFilename());
2772
2773     // no fallback if template file not existing
2774     return;
2775   }
2776
2777   // special case: check for file name/pattern specified in "levelinfo.conf"
2778   if (leveldir_current->level_filename != NULL)
2779   {
2780     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2781
2782     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2783                                          leveldir_current->level_filename, nr);
2784
2785     lfi->packed = checkForPackageFromBasename(leveldir_current->level_filename);
2786
2787     if (fileExists(lfi->filename))
2788       return;
2789   }
2790   else if (leveldir_current->level_filetype != NULL)
2791   {
2792     int filetype = getFiletypeFromID(leveldir_current->level_filetype);
2793
2794     // check for specified native level file with standard file name
2795     setLevelFileInfo_FormatLevelFilename(lfi, filetype,
2796                                          "%03d.%s", nr, LEVELFILE_EXTENSION);
2797     if (fileExists(lfi->filename))
2798       return;
2799   }
2800
2801   // check for native Rocks'n'Diamonds level file
2802   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2803                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2804   if (fileExists(lfi->filename))
2805     return;
2806
2807   // check for native Boulder Dash level file
2808   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_BD, "%03d.bd", nr);
2809   if (fileExists(lfi->filename))
2810     return;
2811
2812   // check for Emerald Mine level file (V1)
2813   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "a%c%c",
2814                                        'a' + (nr / 10) % 26, '0' + nr % 10);
2815   if (fileExists(lfi->filename))
2816     return;
2817   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "A%c%c",
2818                                        'A' + (nr / 10) % 26, '0' + nr % 10);
2819   if (fileExists(lfi->filename))
2820     return;
2821
2822   // check for Emerald Mine level file (V2 to V5)
2823   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%d", nr);
2824   if (fileExists(lfi->filename))
2825     return;
2826
2827   // check for Emerald Mine level file (V6 / single mode)
2828   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02ds", nr);
2829   if (fileExists(lfi->filename))
2830     return;
2831   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dS", nr);
2832   if (fileExists(lfi->filename))
2833     return;
2834
2835   // check for Emerald Mine level file (V6 / teamwork mode)
2836   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dt", nr);
2837   if (fileExists(lfi->filename))
2838     return;
2839   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_EM, "%02dT", nr);
2840   if (fileExists(lfi->filename))
2841     return;
2842
2843   // check for various packed level file formats
2844   setLevelFileInfo_PackedLevelFilename(lfi, LEVEL_FILE_TYPE_UNKNOWN);
2845   if (fileExists(lfi->filename))
2846     return;
2847
2848   // no known level file found -- use default values (and fail later)
2849   setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
2850                                        "%03d.%s", nr, LEVELFILE_EXTENSION);
2851 }
2852
2853 static void determineLevelFileInfo_Filetype(struct LevelFileInfo *lfi)
2854 {
2855   if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
2856     lfi->type = getFileTypeFromBasename(lfi->basename);
2857
2858   if (lfi->type == LEVEL_FILE_TYPE_RND)
2859     lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
2860 }
2861
2862 static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
2863 {
2864   // always start with reliable default values
2865   setFileInfoToDefaults(level_file_info);
2866
2867   level_file_info->nr = nr;     // set requested level number
2868
2869   determineLevelFileInfo_Filename(level_file_info);
2870   determineLevelFileInfo_Filetype(level_file_info);
2871 }
2872
2873 static void copyLevelFileInfo(struct LevelFileInfo *lfi_from,
2874                               struct LevelFileInfo *lfi_to)
2875 {
2876   lfi_to->nr     = lfi_from->nr;
2877   lfi_to->type   = lfi_from->type;
2878   lfi_to->packed = lfi_from->packed;
2879
2880   setString(&lfi_to->basename, lfi_from->basename);
2881   setString(&lfi_to->filename, lfi_from->filename);
2882 }
2883
2884 // ----------------------------------------------------------------------------
2885 // functions for loading R'n'D level
2886 // ----------------------------------------------------------------------------
2887
2888 int getMappedElement(int element)
2889 {
2890   // remap some (historic, now obsolete) elements
2891
2892   switch (element)
2893   {
2894     case EL_PLAYER_OBSOLETE:
2895       element = EL_PLAYER_1;
2896       break;
2897
2898     case EL_KEY_OBSOLETE:
2899       element = EL_KEY_1;
2900       break;
2901
2902     case EL_EM_KEY_1_FILE_OBSOLETE:
2903       element = EL_EM_KEY_1;
2904       break;
2905
2906     case EL_EM_KEY_2_FILE_OBSOLETE:
2907       element = EL_EM_KEY_2;
2908       break;
2909
2910     case EL_EM_KEY_3_FILE_OBSOLETE:
2911       element = EL_EM_KEY_3;
2912       break;
2913
2914     case EL_EM_KEY_4_FILE_OBSOLETE:
2915       element = EL_EM_KEY_4;
2916       break;
2917
2918     case EL_ENVELOPE_OBSOLETE:
2919       element = EL_ENVELOPE_1;
2920       break;
2921
2922     case EL_SP_EMPTY:
2923       element = EL_EMPTY;
2924       break;
2925
2926     default:
2927       if (element >= NUM_FILE_ELEMENTS)
2928       {
2929         Warn("invalid level element %d", element);
2930
2931         element = EL_UNKNOWN;
2932       }
2933       break;
2934   }
2935
2936   return element;
2937 }
2938
2939 static int getMappedElementByVersion(int element, int game_version)
2940 {
2941   // remap some elements due to certain game version
2942
2943   if (game_version <= VERSION_IDENT(2,2,0,0))
2944   {
2945     // map game font elements
2946     element = (element == EL_CHAR('[')  ? EL_CHAR_AUMLAUT :
2947                element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
2948                element == EL_CHAR(']')  ? EL_CHAR_UUMLAUT :
2949                element == EL_CHAR('^')  ? EL_CHAR_COPYRIGHT : element);
2950   }
2951
2952   if (game_version < VERSION_IDENT(3,0,0,0))
2953   {
2954     // map Supaplex gravity tube elements
2955     element = (element == EL_SP_GRAVITY_PORT_LEFT  ? EL_SP_PORT_LEFT  :
2956                element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
2957                element == EL_SP_GRAVITY_PORT_UP    ? EL_SP_PORT_UP    :
2958                element == EL_SP_GRAVITY_PORT_DOWN  ? EL_SP_PORT_DOWN  :
2959                element);
2960   }
2961
2962   return element;
2963 }
2964
2965 static int LoadLevel_VERS(File *file, int chunk_size, struct LevelInfo *level)
2966 {
2967   level->file_version = getFileVersion(file);
2968   level->game_version = getFileVersion(file);
2969
2970   return chunk_size;
2971 }
2972
2973 static int LoadLevel_DATE(File *file, int chunk_size, struct LevelInfo *level)
2974 {
2975   level->creation_date.year  = getFile16BitBE(file);
2976   level->creation_date.month = getFile8Bit(file);
2977   level->creation_date.day   = getFile8Bit(file);
2978
2979   level->creation_date.src   = DATE_SRC_LEVELFILE;
2980
2981   return chunk_size;
2982 }
2983
2984 static int LoadLevel_HEAD(File *file, int chunk_size, struct LevelInfo *level)
2985 {
2986   int initial_player_stepsize;
2987   int initial_player_gravity;
2988   int i, x, y;
2989
2990   level->fieldx = getFile8Bit(file);
2991   level->fieldy = getFile8Bit(file);
2992
2993   level->time           = getFile16BitBE(file);
2994   level->gems_needed    = getFile16BitBE(file);
2995
2996   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
2997     level->name[i] = getFile8Bit(file);
2998   level->name[MAX_LEVEL_NAME_LEN] = 0;
2999
3000   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
3001     level->score[i] = getFile8Bit(file);
3002
3003   level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3004   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
3005     for (y = 0; y < 3; y++)
3006       for (x = 0; x < 3; x++)
3007         level->yamyam_content[i].e[x][y] = getMappedElement(getFile8Bit(file));
3008
3009   level->amoeba_speed           = getFile8Bit(file);
3010   level->time_magic_wall        = getFile8Bit(file);
3011   level->time_wheel             = getFile8Bit(file);
3012   level->amoeba_content         = getMappedElement(getFile8Bit(file));
3013
3014   initial_player_stepsize       = (getFile8Bit(file) == 1 ? STEPSIZE_FAST :
3015                                    STEPSIZE_NORMAL);
3016
3017   for (i = 0; i < MAX_PLAYERS; i++)
3018     level->initial_player_stepsize[i] = initial_player_stepsize;
3019
3020   initial_player_gravity        = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3021
3022   for (i = 0; i < MAX_PLAYERS; i++)
3023     level->initial_player_gravity[i] = initial_player_gravity;
3024
3025   level->encoding_16bit_field   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3026   level->em_slippery_gems       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3027
3028   level->use_custom_template    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3029
3030   level->block_last_field       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3031   level->sp_block_last_field    = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3032   level->can_move_into_acid_bits = getFile32BitBE(file);
3033   level->dont_collide_with_bits = getFile8Bit(file);
3034
3035   level->use_spring_bug         = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3036   level->use_step_counter       = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3037
3038   level->instant_relocation     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3039   level->can_pass_to_walkable   = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3040   level->grow_into_diggable     = (getFile8Bit(file) == 1 ? TRUE : FALSE);
3041
3042   level->game_engine_type       = getFile8Bit(file);
3043
3044   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_HEAD_UNUSED);
3045
3046   return chunk_size;
3047 }
3048
3049 static int LoadLevel_NAME(File *file, int chunk_size, struct LevelInfo *level)
3050 {
3051   int i;
3052
3053   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
3054     level->name[i] = getFile8Bit(file);
3055   level->name[MAX_LEVEL_NAME_LEN] = 0;
3056
3057   return chunk_size;
3058 }
3059
3060 static int LoadLevel_AUTH(File *file, int chunk_size, struct LevelInfo *level)
3061 {
3062   int i;
3063
3064   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
3065     level->author[i] = getFile8Bit(file);
3066   level->author[MAX_LEVEL_AUTHOR_LEN] = 0;
3067
3068   return chunk_size;
3069 }
3070
3071 static int LoadLevel_BODY(File *file, int chunk_size, struct LevelInfo *level)
3072 {
3073   int x, y;
3074   int chunk_size_expected = level->fieldx * level->fieldy;
3075
3076   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3077      stored with 16-bit encoding (and should be twice as big then).
3078      Even worse, playfield data was stored 16-bit when only yamyam content
3079      contained 16-bit elements and vice versa. */
3080
3081   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3082     chunk_size_expected *= 2;
3083
3084   if (chunk_size_expected != chunk_size)
3085   {
3086     ReadUnusedBytesFromFile(file, chunk_size);
3087     return chunk_size_expected;
3088   }
3089
3090   for (y = 0; y < level->fieldy; y++)
3091     for (x = 0; x < level->fieldx; x++)
3092       level->field[x][y] =
3093         getMappedElement(level->encoding_16bit_field ? getFile16BitBE(file) :
3094                          getFile8Bit(file));
3095   return chunk_size;
3096 }
3097
3098 static int LoadLevel_CONT(File *file, int chunk_size, struct LevelInfo *level)
3099 {
3100   int i, x, y;
3101   int header_size = 4;
3102   int content_size = MAX_ELEMENT_CONTENTS * 3 * 3;
3103   int chunk_size_expected = header_size + content_size;
3104
3105   /* Note: "chunk_size" was wrong before version 2.0 when elements are
3106      stored with 16-bit encoding (and should be twice as big then).
3107      Even worse, playfield data was stored 16-bit when only yamyam content
3108      contained 16-bit elements and vice versa. */
3109
3110   if (level->encoding_16bit_field && level->file_version >= FILE_VERSION_2_0)
3111     chunk_size_expected += content_size;
3112
3113   if (chunk_size_expected != chunk_size)
3114   {
3115     ReadUnusedBytesFromFile(file, chunk_size);
3116     return chunk_size_expected;
3117   }
3118
3119   getFile8Bit(file);
3120   level->num_yamyam_contents = getFile8Bit(file);
3121   getFile8Bit(file);
3122   getFile8Bit(file);
3123
3124   // correct invalid number of content fields -- should never happen
3125   if (level->num_yamyam_contents < 1 ||
3126       level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
3127     level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
3128
3129   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3130     for (y = 0; y < 3; y++)
3131       for (x = 0; x < 3; x++)
3132         level->yamyam_content[i].e[x][y] =
3133           getMappedElement(level->encoding_16bit_field ?
3134                            getFile16BitBE(file) : getFile8Bit(file));
3135   return chunk_size;
3136 }
3137
3138 static int LoadLevel_CNT2(File *file, int chunk_size, struct LevelInfo *level)
3139 {
3140   int i, x, y;
3141   int element;
3142   int num_contents;
3143   int content_array[MAX_ELEMENT_CONTENTS][3][3];
3144
3145   element = getMappedElement(getFile16BitBE(file));
3146   num_contents = getFile8Bit(file);
3147
3148   getFile8Bit(file);    // content x size (unused)
3149   getFile8Bit(file);    // content y size (unused)
3150
3151   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
3152
3153   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
3154     for (y = 0; y < 3; y++)
3155       for (x = 0; x < 3; x++)
3156         content_array[i][x][y] = getMappedElement(getFile16BitBE(file));
3157
3158   // correct invalid number of content fields -- should never happen
3159   if (num_contents < 1 || num_contents > MAX_ELEMENT_CONTENTS)
3160     num_contents = STD_ELEMENT_CONTENTS;
3161
3162   if (element == EL_YAMYAM)
3163   {
3164     level->num_yamyam_contents = num_contents;
3165
3166     for (i = 0; i < num_contents; i++)
3167       for (y = 0; y < 3; y++)
3168         for (x = 0; x < 3; x++)
3169           level->yamyam_content[i].e[x][y] = content_array[i][x][y];
3170   }
3171   else if (element == EL_BD_AMOEBA)
3172   {
3173     level->amoeba_content = content_array[0][0][0];
3174   }
3175   else
3176   {
3177     Warn("cannot load content for element '%d'", element);
3178   }
3179
3180   return chunk_size;
3181 }
3182
3183 static int LoadLevel_CNT3(File *file, int chunk_size, struct LevelInfo *level)
3184 {
3185   int i;
3186   int element;
3187   int envelope_nr;
3188   int envelope_len;
3189   int chunk_size_expected;
3190
3191   element = getMappedElement(getFile16BitBE(file));
3192   if (!IS_ENVELOPE(element))
3193     element = EL_ENVELOPE_1;
3194
3195   envelope_nr = element - EL_ENVELOPE_1;
3196
3197   envelope_len = getFile16BitBE(file);
3198
3199   level->envelope[envelope_nr].xsize = getFile8Bit(file);
3200   level->envelope[envelope_nr].ysize = getFile8Bit(file);
3201
3202   ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT3_UNUSED);
3203
3204   chunk_size_expected = LEVEL_CHUNK_CNT3_SIZE(envelope_len);
3205   if (chunk_size_expected != chunk_size)
3206   {
3207     ReadUnusedBytesFromFile(file, chunk_size - LEVEL_CHUNK_CNT3_HEADER);
3208     return chunk_size_expected;
3209   }
3210
3211   for (i = 0; i < envelope_len; i++)
3212     level->envelope[envelope_nr].text[i] = getFile8Bit(file);
3213
3214   return chunk_size;
3215 }
3216
3217 static int LoadLevel_CUS1(File *file, int chunk_size, struct LevelInfo *level)
3218 {
3219   int num_changed_custom_elements = getFile16BitBE(file);
3220   int chunk_size_expected = 2 + num_changed_custom_elements * 6;
3221   int i;
3222
3223   if (chunk_size_expected != chunk_size)
3224   {
3225     ReadUnusedBytesFromFile(file, chunk_size - 2);
3226     return chunk_size_expected;
3227   }
3228
3229   for (i = 0; i < num_changed_custom_elements; i++)
3230   {
3231     int element = getMappedElement(getFile16BitBE(file));
3232     int properties = getFile32BitBE(file);
3233
3234     if (IS_CUSTOM_ELEMENT(element))
3235       element_info[element].properties[EP_BITFIELD_BASE_NR] = properties;
3236     else
3237       Warn("invalid custom element number %d", element);
3238
3239     // older game versions that wrote level files with CUS1 chunks used
3240     // different default push delay values (not yet stored in level file)
3241     element_info[element].push_delay_fixed = 2;
3242     element_info[element].push_delay_random = 8;
3243   }
3244
3245   level->file_has_custom_elements = TRUE;
3246
3247   return chunk_size;
3248 }
3249
3250 static int LoadLevel_CUS2(File *file, int chunk_size, struct LevelInfo *level)
3251 {
3252   int num_changed_custom_elements = getFile16BitBE(file);
3253   int chunk_size_expected = 2 + num_changed_custom_elements * 4;
3254   int i;
3255
3256   if (chunk_size_expected != chunk_size)
3257   {
3258     ReadUnusedBytesFromFile(file, chunk_size - 2);
3259     return chunk_size_expected;
3260   }
3261
3262   for (i = 0; i < num_changed_custom_elements; i++)
3263   {
3264     int element = getMappedElement(getFile16BitBE(file));
3265     int custom_target_element = getMappedElement(getFile16BitBE(file));
3266
3267     if (IS_CUSTOM_ELEMENT(element))
3268       element_info[element].change->target_element = custom_target_element;
3269     else
3270       Warn("invalid custom element number %d", element);
3271   }
3272
3273   level->file_has_custom_elements = TRUE;
3274
3275   return chunk_size;
3276 }
3277
3278 static int LoadLevel_CUS3(File *file, int chunk_size, struct LevelInfo *level)
3279 {
3280   int num_changed_custom_elements = getFile16BitBE(file);
3281   int chunk_size_expected = LEVEL_CHUNK_CUS3_SIZE(num_changed_custom_elements);
3282   int i, j, x, y;
3283
3284   if (chunk_size_expected != chunk_size)
3285   {
3286     ReadUnusedBytesFromFile(file, chunk_size - 2);
3287     return chunk_size_expected;
3288   }
3289
3290   for (i = 0; i < num_changed_custom_elements; i++)
3291   {
3292     int element = getMappedElement(getFile16BitBE(file));
3293     struct ElementInfo *ei = &element_info[element];
3294     unsigned int event_bits;
3295
3296     if (!IS_CUSTOM_ELEMENT(element))
3297     {
3298       Warn("invalid custom element number %d", element);
3299
3300       element = EL_INTERNAL_DUMMY;
3301     }
3302
3303     for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
3304       ei->description[j] = getFile8Bit(file);
3305     ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3306
3307     ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3308
3309     // some free bytes for future properties and padding
3310     ReadUnusedBytesFromFile(file, 7);
3311
3312     ei->use_gfx_element = getFile8Bit(file);
3313     ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3314
3315     ei->collect_score_initial = getFile8Bit(file);
3316     ei->collect_count_initial = getFile8Bit(file);
3317
3318     ei->push_delay_fixed = getFile16BitBE(file);
3319     ei->push_delay_random = getFile16BitBE(file);
3320     ei->move_delay_fixed = getFile16BitBE(file);
3321     ei->move_delay_random = getFile16BitBE(file);
3322
3323     ei->move_pattern = getFile16BitBE(file);
3324     ei->move_direction_initial = getFile8Bit(file);
3325     ei->move_stepsize = getFile8Bit(file);
3326
3327     for (y = 0; y < 3; y++)
3328       for (x = 0; x < 3; x++)
3329         ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3330
3331     // bits 0 - 31 of "has_event[]"
3332     event_bits = getFile32BitBE(file);
3333     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3334       if (event_bits & (1u << j))
3335         ei->change->has_event[j] = TRUE;
3336
3337     ei->change->target_element = getMappedElement(getFile16BitBE(file));
3338
3339     ei->change->delay_fixed = getFile16BitBE(file);
3340     ei->change->delay_random = getFile16BitBE(file);
3341     ei->change->delay_frames = getFile16BitBE(file);
3342
3343     ei->change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3344
3345     ei->change->explode = getFile8Bit(file);
3346     ei->change->use_target_content = getFile8Bit(file);
3347     ei->change->only_if_complete = getFile8Bit(file);
3348     ei->change->use_random_replace = getFile8Bit(file);
3349
3350     ei->change->random_percentage = getFile8Bit(file);
3351     ei->change->replace_when = getFile8Bit(file);
3352
3353     for (y = 0; y < 3; y++)
3354       for (x = 0; x < 3; x++)
3355         ei->change->target_content.e[x][y] =
3356           getMappedElement(getFile16BitBE(file));
3357
3358     ei->slippery_type = getFile8Bit(file);
3359
3360     // some free bytes for future properties and padding
3361     ReadUnusedBytesFromFile(file, LEVEL_CPART_CUS3_UNUSED);
3362
3363     // mark that this custom element has been modified
3364     ei->modified_settings = TRUE;
3365   }
3366
3367   level->file_has_custom_elements = TRUE;
3368
3369   return chunk_size;
3370 }
3371
3372 static int LoadLevel_CUS4(File *file, int chunk_size, struct LevelInfo *level)
3373 {
3374   struct ElementInfo *ei;
3375   int chunk_size_expected;
3376   int element;
3377   int i, j, x, y;
3378
3379   // ---------- custom element base property values (96 bytes) ----------------
3380
3381   element = getMappedElement(getFile16BitBE(file));
3382
3383   if (!IS_CUSTOM_ELEMENT(element))
3384   {
3385     Warn("invalid custom element number %d", element);
3386
3387     ReadUnusedBytesFromFile(file, chunk_size - 2);
3388
3389     return chunk_size;
3390   }
3391
3392   ei = &element_info[element];
3393
3394   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3395     ei->description[i] = getFile8Bit(file);
3396   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3397
3398   ei->properties[EP_BITFIELD_BASE_NR] = getFile32BitBE(file);
3399
3400   ReadUnusedBytesFromFile(file, 4);     // reserved for more base properties
3401
3402   ei->num_change_pages = getFile8Bit(file);
3403
3404   chunk_size_expected = LEVEL_CHUNK_CUS4_SIZE(ei->num_change_pages);
3405   if (chunk_size_expected != chunk_size)
3406   {
3407     ReadUnusedBytesFromFile(file, chunk_size - 43);
3408     return chunk_size_expected;
3409   }
3410
3411   ei->ce_value_fixed_initial = getFile16BitBE(file);
3412   ei->ce_value_random_initial = getFile16BitBE(file);
3413   ei->use_last_ce_value = getFile8Bit(file);
3414
3415   ei->use_gfx_element = getFile8Bit(file);
3416   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3417
3418   ei->collect_score_initial = getFile8Bit(file);
3419   ei->collect_count_initial = getFile8Bit(file);
3420
3421   ei->drop_delay_fixed = getFile8Bit(file);
3422   ei->push_delay_fixed = getFile8Bit(file);
3423   ei->drop_delay_random = getFile8Bit(file);
3424   ei->push_delay_random = getFile8Bit(file);
3425   ei->move_delay_fixed = getFile16BitBE(file);
3426   ei->move_delay_random = getFile16BitBE(file);
3427
3428   // bits 0 - 15 of "move_pattern" ...
3429   ei->move_pattern = getFile16BitBE(file);
3430   ei->move_direction_initial = getFile8Bit(file);
3431   ei->move_stepsize = getFile8Bit(file);
3432
3433   ei->slippery_type = getFile8Bit(file);
3434
3435   for (y = 0; y < 3; y++)
3436     for (x = 0; x < 3; x++)
3437       ei->content.e[x][y] = getMappedElement(getFile16BitBE(file));
3438
3439   ei->move_enter_element = getMappedElement(getFile16BitBE(file));
3440   ei->move_leave_element = getMappedElement(getFile16BitBE(file));
3441   ei->move_leave_type = getFile8Bit(file);
3442
3443   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
3444   ei->move_pattern |= (getFile16BitBE(file) << 16);
3445
3446   ei->access_direction = getFile8Bit(file);
3447
3448   ei->explosion_delay = getFile8Bit(file);
3449   ei->ignition_delay = getFile8Bit(file);
3450   ei->explosion_type = getFile8Bit(file);
3451
3452   // some free bytes for future custom property values and padding
3453   ReadUnusedBytesFromFile(file, 1);
3454
3455   // ---------- change page property values (48 bytes) ------------------------
3456
3457   setElementChangePages(ei, ei->num_change_pages);
3458
3459   for (i = 0; i < ei->num_change_pages; i++)
3460   {
3461     struct ElementChangeInfo *change = &ei->change_page[i];
3462     unsigned int event_bits;
3463
3464     // always start with reliable default values
3465     setElementChangeInfoToDefaults(change);
3466
3467     // bits 0 - 31 of "has_event[]" ...
3468     event_bits = getFile32BitBE(file);
3469     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
3470       if (event_bits & (1u << j))
3471         change->has_event[j] = TRUE;
3472
3473     change->target_element = getMappedElement(getFile16BitBE(file));
3474
3475     change->delay_fixed = getFile16BitBE(file);
3476     change->delay_random = getFile16BitBE(file);
3477     change->delay_frames = getFile16BitBE(file);
3478
3479     change->initial_trigger_element = getMappedElement(getFile16BitBE(file));
3480
3481     change->explode = getFile8Bit(file);
3482     change->use_target_content = getFile8Bit(file);
3483     change->only_if_complete = getFile8Bit(file);
3484     change->use_random_replace = getFile8Bit(file);
3485
3486     change->random_percentage = getFile8Bit(file);
3487     change->replace_when = getFile8Bit(file);
3488
3489     for (y = 0; y < 3; y++)
3490       for (x = 0; x < 3; x++)
3491         change->target_content.e[x][y]= getMappedElement(getFile16BitBE(file));
3492
3493     change->can_change = getFile8Bit(file);
3494
3495     change->trigger_side = getFile8Bit(file);
3496
3497     change->trigger_player = getFile8Bit(file);
3498     change->trigger_page = getFile8Bit(file);
3499
3500     change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
3501                             CH_PAGE_ANY : (1 << change->trigger_page));
3502
3503     change->has_action = getFile8Bit(file);
3504     change->action_type = getFile8Bit(file);
3505     change->action_mode = getFile8Bit(file);
3506     change->action_arg = getFile16BitBE(file);
3507
3508     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
3509     event_bits = getFile8Bit(file);
3510     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
3511       if (event_bits & (1u << (j - 32)))
3512         change->has_event[j] = TRUE;
3513   }
3514
3515   // mark this custom element as modified
3516   ei->modified_settings = TRUE;
3517
3518   level->file_has_custom_elements = TRUE;
3519
3520   return chunk_size;
3521 }
3522
3523 static int LoadLevel_GRP1(File *file, int chunk_size, struct LevelInfo *level)
3524 {
3525   struct ElementInfo *ei;
3526   struct ElementGroupInfo *group;
3527   int element;
3528   int i;
3529
3530   element = getMappedElement(getFile16BitBE(file));
3531
3532   if (!IS_GROUP_ELEMENT(element))
3533   {
3534     Warn("invalid group element number %d", element);
3535
3536     ReadUnusedBytesFromFile(file, chunk_size - 2);
3537
3538     return chunk_size;
3539   }
3540
3541   ei = &element_info[element];
3542
3543   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
3544     ei->description[i] = getFile8Bit(file);
3545   ei->description[MAX_ELEMENT_NAME_LEN] = 0;
3546
3547   group = element_info[element].group;
3548
3549   group->num_elements = getFile8Bit(file);
3550
3551   ei->use_gfx_element = getFile8Bit(file);
3552   ei->gfx_element_initial = getMappedElement(getFile16BitBE(file));
3553
3554   group->choice_mode = getFile8Bit(file);
3555
3556   // some free bytes for future values and padding
3557   ReadUnusedBytesFromFile(file, 3);
3558
3559   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
3560     group->element[i] = getMappedElement(getFile16BitBE(file));
3561
3562   // mark this group element as modified
3563   element_info[element].modified_settings = TRUE;
3564
3565   level->file_has_custom_elements = TRUE;
3566
3567   return chunk_size;
3568 }
3569
3570 static int LoadLevel_MicroChunk(File *file, struct LevelFileConfigInfo *conf,
3571                                 int element, int real_element)
3572 {
3573   int micro_chunk_size = 0;
3574   int conf_type = getFile8Bit(file);
3575   int byte_mask = conf_type & CONF_MASK_BYTES;
3576   boolean element_found = FALSE;
3577   int i;
3578
3579   micro_chunk_size += 1;
3580
3581   if (byte_mask == CONF_MASK_MULTI_BYTES)
3582   {
3583     int num_bytes = getFile16BitBE(file);
3584     byte *buffer = checked_malloc(num_bytes);
3585
3586     ReadBytesFromFile(file, buffer, num_bytes);
3587
3588     for (i = 0; conf[i].data_type != -1; i++)
3589     {
3590       if (conf[i].element == element &&
3591           conf[i].conf_type == conf_type)
3592       {
3593         int data_type = conf[i].data_type;
3594         int num_entities = num_bytes / CONF_ENTITY_NUM_BYTES(data_type);
3595         int max_num_entities = conf[i].max_num_entities;
3596
3597         if (num_entities > max_num_entities)
3598         {
3599           Warn("truncating number of entities for element %d from %d to %d",
3600                element, num_entities, max_num_entities);
3601
3602           num_entities = max_num_entities;
3603         }
3604
3605         if (num_entities == 0 && (data_type == TYPE_ELEMENT_LIST ||
3606                                   data_type == TYPE_CONTENT_LIST))
3607         {
3608           // for element and content lists, zero entities are not allowed
3609           Warn("found empty list of entities for element %d", element);
3610
3611           // do not set "num_entities" here to prevent reading behind buffer
3612
3613           *(int *)(conf[i].num_entities) = 1;   // at least one is required
3614         }
3615         else
3616         {
3617           *(int *)(conf[i].num_entities) = num_entities;
3618         }
3619
3620         element_found = TRUE;
3621
3622         if (data_type == TYPE_STRING)
3623         {
3624           char *string = (char *)(conf[i].value);
3625           int j;
3626
3627           for (j = 0; j < max_num_entities; j++)
3628             string[j] = (j < num_entities ? buffer[j] : '\0');
3629         }
3630         else if (data_type == TYPE_ELEMENT_LIST)
3631         {
3632           int *element_array = (int *)(conf[i].value);
3633           int j;
3634
3635           for (j = 0; j < num_entities; j++)
3636             element_array[j] =
3637               getMappedElement(CONF_ELEMENTS_ELEMENT(buffer, j));
3638         }
3639         else if (data_type == TYPE_CONTENT_LIST)
3640         {
3641           struct Content *content= (struct Content *)(conf[i].value);
3642           int c, x, y;
3643
3644           for (c = 0; c < num_entities; c++)
3645             for (y = 0; y < 3; y++)
3646               for (x = 0; x < 3; x++)
3647                 content[c].e[x][y] =
3648                   getMappedElement(CONF_CONTENTS_ELEMENT(buffer, c, x, y));
3649         }
3650         else
3651           element_found = FALSE;
3652
3653         break;
3654       }
3655     }
3656
3657     checked_free(buffer);
3658
3659     micro_chunk_size += 2 + num_bytes;
3660   }
3661   else          // constant size configuration data (1, 2 or 4 bytes)
3662   {
3663     int value = (byte_mask == CONF_MASK_1_BYTE ? getFile8Bit   (file) :
3664                  byte_mask == CONF_MASK_2_BYTE ? getFile16BitBE(file) :
3665                  byte_mask == CONF_MASK_4_BYTE ? getFile32BitBE(file) : 0);
3666
3667     for (i = 0; conf[i].data_type != -1; i++)
3668     {
3669       if (conf[i].element == element &&
3670           conf[i].conf_type == conf_type)
3671       {
3672         int data_type = conf[i].data_type;
3673
3674         if (data_type == TYPE_ELEMENT)
3675           value = getMappedElement(value);
3676
3677         if (data_type == TYPE_BOOLEAN)
3678           *(boolean *)(conf[i].value) = (value ? TRUE : FALSE);
3679         else
3680           *(int *)    (conf[i].value) = value;
3681
3682         element_found = TRUE;
3683
3684         break;
3685       }
3686     }
3687
3688     micro_chunk_size += CONF_VALUE_NUM_BYTES(byte_mask);
3689   }
3690
3691   if (!element_found)
3692   {
3693     char *error_conf_chunk_bytes =
3694       (byte_mask == CONF_MASK_1_BYTE ? "CONF_VALUE_8_BIT" :
3695        byte_mask == CONF_MASK_2_BYTE ? "CONF_VALUE_16_BIT" :
3696        byte_mask == CONF_MASK_4_BYTE ? "CONF_VALUE_32_BIT" :"CONF_VALUE_BYTES");
3697     int error_conf_chunk_token = conf_type & CONF_MASK_TOKEN;
3698     int error_element = real_element;
3699
3700     Warn("cannot load micro chunk '%s(%d)' value for element %d ['%s']",
3701          error_conf_chunk_bytes, error_conf_chunk_token,
3702          error_element, EL_NAME(error_element));
3703   }
3704
3705   return micro_chunk_size;
3706 }
3707
3708 static int LoadLevel_INFO(File *file, int chunk_size, struct LevelInfo *level)
3709 {
3710   int real_chunk_size = 0;
3711
3712   li = *level;          // copy level data into temporary buffer
3713
3714   while (!checkEndOfFile(file))
3715   {
3716     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_INFO, -1, -1);
3717
3718     if (real_chunk_size >= chunk_size)
3719       break;
3720   }
3721
3722   *level = li;          // copy temporary buffer back to level data
3723
3724   return real_chunk_size;
3725 }
3726
3727 static int LoadLevel_CONF(File *file, int chunk_size, struct LevelInfo *level)
3728 {
3729   int real_chunk_size = 0;
3730
3731   li = *level;          // copy level data into temporary buffer
3732
3733   while (!checkEndOfFile(file))
3734   {
3735     int element = getMappedElement(getFile16BitBE(file));
3736
3737     real_chunk_size += 2;
3738     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CONF,
3739                                             element, element);
3740     if (real_chunk_size >= chunk_size)
3741       break;
3742   }
3743
3744   *level = li;          // copy temporary buffer back to level data
3745
3746   return real_chunk_size;
3747 }
3748
3749 static int LoadLevel_ELEM(File *file, int chunk_size, struct LevelInfo *level)
3750 {
3751   int real_chunk_size = 0;
3752
3753   li = *level;          // copy level data into temporary buffer
3754
3755   while (!checkEndOfFile(file))
3756   {
3757     int element = getMappedElement(getFile16BitBE(file));
3758
3759     real_chunk_size += 2;
3760     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_ELEM,
3761                                             element, element);
3762     if (real_chunk_size >= chunk_size)
3763       break;
3764   }
3765
3766   *level = li;          // copy temporary buffer back to level data
3767
3768   return real_chunk_size;
3769 }
3770
3771 static int LoadLevel_NOTE(File *file, int chunk_size, struct LevelInfo *level)
3772 {
3773   int element = getMappedElement(getFile16BitBE(file));
3774   int envelope_nr = element - EL_ENVELOPE_1;
3775   int real_chunk_size = 2;
3776
3777   xx_envelope = level->envelope[envelope_nr];   // copy into temporary buffer
3778
3779   while (!checkEndOfFile(file))
3780   {
3781     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_NOTE,
3782                                             -1, element);
3783
3784     if (real_chunk_size >= chunk_size)
3785       break;
3786   }
3787
3788   level->envelope[envelope_nr] = xx_envelope;   // copy from temporary buffer
3789
3790   return real_chunk_size;
3791 }
3792
3793 static int LoadLevel_CUSX(File *file, int chunk_size, struct LevelInfo *level)
3794 {
3795   int element = getMappedElement(getFile16BitBE(file));
3796   int real_chunk_size = 2;
3797   struct ElementInfo *ei = &element_info[element];
3798   int i;
3799
3800   xx_ei = *ei;          // copy element data into temporary buffer
3801
3802   xx_ei.num_change_pages = -1;
3803
3804   while (!checkEndOfFile(file))
3805   {
3806     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_base,
3807                                             -1, element);
3808     if (xx_ei.num_change_pages != -1)
3809       break;
3810
3811     if (real_chunk_size >= chunk_size)
3812       break;
3813   }
3814
3815   *ei = xx_ei;
3816
3817   if (ei->num_change_pages == -1)
3818   {
3819     Warn("LoadLevel_CUSX(): missing 'num_change_pages' for '%s'",
3820          EL_NAME(element));
3821
3822     ei->num_change_pages = 1;
3823
3824     setElementChangePages(ei, 1);
3825     setElementChangeInfoToDefaults(ei->change);
3826
3827     return real_chunk_size;
3828   }
3829
3830   // initialize number of change pages stored for this custom element
3831   setElementChangePages(ei, ei->num_change_pages);
3832   for (i = 0; i < ei->num_change_pages; i++)
3833     setElementChangeInfoToDefaults(&ei->change_page[i]);
3834
3835   // start with reading properties for the first change page
3836   xx_current_change_page = 0;
3837
3838   while (!checkEndOfFile(file))
3839   {
3840     // level file might contain invalid change page number
3841     if (xx_current_change_page >= ei->num_change_pages)
3842       break;
3843
3844     struct ElementChangeInfo *change = &ei->change_page[xx_current_change_page];
3845
3846     xx_change = *change;        // copy change data into temporary buffer
3847
3848     resetEventBits();           // reset bits; change page might have changed
3849
3850     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_CUSX_change,
3851                                             -1, element);
3852
3853     *change = xx_change;
3854
3855     setEventFlagsFromEventBits(change);
3856
3857     if (real_chunk_size >= chunk_size)
3858       break;
3859   }
3860
3861   level->file_has_custom_elements = TRUE;
3862
3863   return real_chunk_size;
3864 }
3865
3866 static int LoadLevel_GRPX(File *file, int chunk_size, struct LevelInfo *level)
3867 {
3868   int element = getMappedElement(getFile16BitBE(file));
3869   int real_chunk_size = 2;
3870   struct ElementInfo *ei = &element_info[element];
3871   struct ElementGroupInfo *group = ei->group;
3872
3873   if (group == NULL)
3874     return -1;
3875
3876   xx_ei = *ei;          // copy element data into temporary buffer
3877   xx_group = *group;    // copy group data into temporary buffer
3878
3879   while (!checkEndOfFile(file))
3880   {
3881     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_GRPX,
3882                                             -1, element);
3883
3884     if (real_chunk_size >= chunk_size)
3885       break;
3886   }
3887
3888   *ei = xx_ei;
3889   *group = xx_group;
3890
3891   level->file_has_custom_elements = TRUE;
3892
3893   return real_chunk_size;
3894 }
3895
3896 static int LoadLevel_EMPX(File *file, int chunk_size, struct LevelInfo *level)
3897 {
3898   int element = getMappedElement(getFile16BitBE(file));
3899   int real_chunk_size = 2;
3900   struct ElementInfo *ei = &element_info[element];
3901
3902   xx_ei = *ei;          // copy element data into temporary buffer
3903
3904   while (!checkEndOfFile(file))
3905   {
3906     real_chunk_size += LoadLevel_MicroChunk(file, chunk_config_EMPX,
3907                                             -1, element);
3908
3909     if (real_chunk_size >= chunk_size)
3910       break;
3911   }
3912
3913   *ei = xx_ei;
3914
3915   level->file_has_custom_elements = TRUE;
3916
3917   return real_chunk_size;
3918 }
3919
3920 static void LoadLevelFromFileInfo_RND(struct LevelInfo *level,
3921                                       struct LevelFileInfo *level_file_info,
3922                                       boolean level_info_only)
3923 {
3924   char *filename = level_file_info->filename;
3925   char cookie[MAX_LINE_LEN];
3926   char chunk_name[CHUNK_ID_LEN + 1];
3927   int chunk_size;
3928   File *file;
3929
3930   if (!(file = openFile(filename, MODE_READ)))
3931   {
3932     level->no_valid_file = TRUE;
3933     level->no_level_file = TRUE;
3934
3935     if (level_info_only)
3936       return;
3937
3938     Warn("cannot read level '%s' -- using empty level", filename);
3939
3940     if (!setup.editor.use_template_for_new_levels)
3941       return;
3942
3943     // if level file not found, try to initialize level data from template
3944     filename = getGlobalLevelTemplateFilename();
3945
3946     if (!(file = openFile(filename, MODE_READ)))
3947       return;
3948
3949     // default: for empty levels, use level template for custom elements
3950     level->use_custom_template = TRUE;
3951
3952     level->no_valid_file = FALSE;
3953   }
3954
3955   getFileChunkBE(file, chunk_name, NULL);
3956   if (strEqual(chunk_name, "RND1"))
3957   {
3958     getFile32BitBE(file);               // not used
3959
3960     getFileChunkBE(file, chunk_name, NULL);
3961     if (!strEqual(chunk_name, "CAVE"))
3962     {
3963       level->no_valid_file = TRUE;
3964
3965       Warn("unknown format of level file '%s'", filename);
3966
3967       closeFile(file);
3968
3969       return;
3970     }
3971   }
3972   else  // check for pre-2.0 file format with cookie string
3973   {
3974     strcpy(cookie, chunk_name);
3975     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
3976       cookie[4] = '\0';
3977     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
3978       cookie[strlen(cookie) - 1] = '\0';
3979
3980     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
3981     {
3982       level->no_valid_file = TRUE;
3983
3984       Warn("unknown format of level file '%s'", filename);
3985
3986       closeFile(file);
3987
3988       return;
3989     }
3990
3991     if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
3992     {
3993       level->no_valid_file = TRUE;
3994
3995       Warn("unsupported version of level file '%s'", filename);
3996
3997       closeFile(file);
3998
3999       return;
4000     }
4001
4002     // pre-2.0 level files have no game version, so use file version here
4003     level->game_version = level->file_version;
4004   }
4005
4006   if (level->file_version < FILE_VERSION_1_2)
4007   {
4008     // level files from versions before 1.2.0 without chunk structure
4009     LoadLevel_HEAD(file, LEVEL_CHUNK_HEAD_SIZE,         level);
4010     LoadLevel_BODY(file, level->fieldx * level->fieldy, level);
4011   }
4012   else
4013   {
4014     static struct
4015     {
4016       char *name;
4017       int size;
4018       int (*loader)(File *, int, struct LevelInfo *);
4019     }
4020     chunk_info[] =
4021     {
4022       { "VERS", LEVEL_CHUNK_VERS_SIZE,  LoadLevel_VERS },
4023       { "DATE", LEVEL_CHUNK_DATE_SIZE,  LoadLevel_DATE },
4024       { "HEAD", LEVEL_CHUNK_HEAD_SIZE,  LoadLevel_HEAD },
4025       { "NAME", LEVEL_CHUNK_NAME_SIZE,  LoadLevel_NAME },
4026       { "AUTH", LEVEL_CHUNK_AUTH_SIZE,  LoadLevel_AUTH },
4027       { "INFO", -1,                     LoadLevel_INFO },
4028       { "BODY", -1,                     LoadLevel_BODY },
4029       { "CONT", -1,                     LoadLevel_CONT },
4030       { "CNT2", LEVEL_CHUNK_CNT2_SIZE,  LoadLevel_CNT2 },
4031       { "CNT3", -1,                     LoadLevel_CNT3 },
4032       { "CUS1", -1,                     LoadLevel_CUS1 },
4033       { "CUS2", -1,                     LoadLevel_CUS2 },
4034       { "CUS3", -1,                     LoadLevel_CUS3 },
4035       { "CUS4", -1,                     LoadLevel_CUS4 },
4036       { "GRP1", -1,                     LoadLevel_GRP1 },
4037       { "CONF", -1,                     LoadLevel_CONF },
4038       { "ELEM", -1,                     LoadLevel_ELEM },
4039       { "NOTE", -1,                     LoadLevel_NOTE },
4040       { "CUSX", -1,                     LoadLevel_CUSX },
4041       { "GRPX", -1,                     LoadLevel_GRPX },
4042       { "EMPX", -1,                     LoadLevel_EMPX },
4043
4044       {  NULL,  0,                      NULL }
4045     };
4046
4047     while (getFileChunkBE(file, chunk_name, &chunk_size))
4048     {
4049       int i = 0;
4050
4051       while (chunk_info[i].name != NULL &&
4052              !strEqual(chunk_name, chunk_info[i].name))
4053         i++;
4054
4055       if (chunk_info[i].name == NULL)
4056       {
4057         Warn("unknown chunk '%s' in level file '%s'",
4058              chunk_name, filename);
4059
4060         ReadUnusedBytesFromFile(file, chunk_size);
4061       }
4062       else if (chunk_info[i].size != -1 &&
4063                chunk_info[i].size != chunk_size)
4064       {
4065         Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4066              chunk_size, chunk_name, filename);
4067
4068         ReadUnusedBytesFromFile(file, chunk_size);
4069       }
4070       else
4071       {
4072         // call function to load this level chunk
4073         int chunk_size_expected =
4074           (chunk_info[i].loader)(file, chunk_size, level);
4075
4076         if (chunk_size_expected < 0)
4077         {
4078           Warn("error reading chunk '%s' in level file '%s'",
4079                chunk_name, filename);
4080
4081           break;
4082         }
4083
4084         // the size of some chunks cannot be checked before reading other
4085         // chunks first (like "HEAD" and "BODY") that contain some header
4086         // information, so check them here
4087         if (chunk_size_expected != chunk_size)
4088         {
4089           Warn("wrong size (%d) of chunk '%s' in level file '%s'",
4090                chunk_size, chunk_name, filename);
4091
4092           break;
4093         }
4094       }
4095     }
4096   }
4097
4098   closeFile(file);
4099 }
4100
4101
4102 // ----------------------------------------------------------------------------
4103 // functions for loading BD level
4104 // ----------------------------------------------------------------------------
4105
4106 #define LEVEL_TO_CAVE(e)        (map_element_RND_to_BD_cave(e))
4107 #define CAVE_TO_LEVEL(e)        (map_element_BD_to_RND_cave(e))
4108
4109 static void CopyNativeLevel_RND_to_BD(struct LevelInfo *level)
4110 {
4111   struct LevelInfo_BD *level_bd = level->native_bd_level;
4112   GdCave *cave = NULL;  // will be changed below
4113   int cave_w = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4114   int cave_h = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4115   int x, y;
4116
4117   setLevelInfoToDefaults_BD_Ext(cave_w, cave_h);
4118
4119   // cave and map newly allocated when set to defaults above
4120   cave = level_bd->cave;
4121
4122   // level type
4123   cave->intermission                    = level->bd_intermission;
4124
4125   // level settings
4126   cave->level_time[0]                   = level->time;
4127   cave->level_diamonds[0]               = level->gems_needed;
4128
4129   // game timing
4130   cave->scheduling                      = level->bd_scheduling_type;
4131   cave->pal_timing                      = level->bd_pal_timing;
4132   cave->level_speed[0]                  = level->bd_cycle_delay_ms;
4133   cave->level_ckdelay[0]                = level->bd_cycle_delay_c64;
4134   cave->level_hatching_delay_frame[0]   = level->bd_hatching_delay_cycles;
4135   cave->level_hatching_delay_time[0]    = level->bd_hatching_delay_seconds;
4136
4137   // scores
4138   cave->level_timevalue[0]              = level->score[SC_TIME_BONUS];
4139   cave->diamond_value                   = level->score[SC_EMERALD];
4140   cave->extra_diamond_value             = level->score[SC_DIAMOND_EXTRA];
4141
4142   // compatibility settings
4143   cave->lineshift                       = level->bd_line_shifting_borders;
4144   cave->border_scan_first_and_last      = level->bd_scan_first_and_last_row;
4145   cave->short_explosions                = level->bd_short_explosions;
4146   cave->gravity_affects_all             = level->bd_gravity_affects_all;
4147
4148   // player properties
4149   cave->diagonal_movements              = level->bd_diagonal_movements;
4150   cave->active_is_first_found           = level->bd_topmost_player_active;
4151   cave->pushing_stone_prob              = level->bd_pushing_prob            * 10000;
4152   cave->pushing_stone_prob_sweet        = level->bd_pushing_prob_with_sweet * 10000;
4153   cave->mega_stones_pushable_with_sweet = level->bd_push_mega_rock_with_sweet;
4154   cave->snap_element                    = LEVEL_TO_CAVE(level->bd_snap_element);
4155
4156   // element properties
4157   cave->level_bonus_time[0]             = level->bd_clock_extra_time;
4158   cave->voodoo_collects_diamonds        = level->bd_voodoo_collects_diamonds;
4159   cave->voodoo_any_hurt_kills_player    = level->bd_voodoo_hurt_kills_player;
4160   cave->voodoo_dies_by_stone            = level->bd_voodoo_dies_by_rock;
4161   cave->voodoo_disappear_in_explosion   = level->bd_voodoo_vanish_by_explosion;
4162   cave->level_penalty_time[0]           = level->bd_voodoo_penalty_time;
4163   cave->level_magic_wall_time[0]        = level->time_magic_wall;
4164   cave->magic_timer_wait_for_hatching   = level->bd_magic_wall_wait_hatching;
4165   cave->magic_wall_stops_amoeba         = level->bd_magic_wall_stops_amoeba;
4166
4167   cave->magic_diamond_to                = LEVEL_TO_CAVE(level->bd_magic_wall_diamond_to);
4168   cave->magic_stone_to                  = LEVEL_TO_CAVE(level->bd_magic_wall_rock_to);
4169   cave->magic_mega_stone_to             = LEVEL_TO_CAVE(level->bd_magic_wall_mega_rock_to);
4170   cave->magic_nut_to                    = LEVEL_TO_CAVE(level->bd_magic_wall_nut_to);
4171   cave->magic_nitro_pack_to             = LEVEL_TO_CAVE(level->bd_magic_wall_nitro_pack_to);
4172   cave->magic_flying_diamond_to         = LEVEL_TO_CAVE(level->bd_magic_wall_flying_diamond_to);
4173   cave->magic_flying_stone_to           = LEVEL_TO_CAVE(level->bd_magic_wall_flying_rock_to);
4174
4175   cave->amoeba_timer_wait_for_hatching  = level->bd_amoeba_wait_for_hatching;
4176   cave->amoeba_timer_started_immediately= level->bd_amoeba_start_immediately;
4177   cave->amoeba_2_explodes_by_amoeba     = level->bd_amoeba_2_explode_by_amoeba;
4178   cave->level_amoeba_threshold[0]       = level->bd_amoeba_threshold_too_big;
4179   cave->level_amoeba_time[0]            = level->bd_amoeba_slow_growth_time;
4180   cave->amoeba_growth_prob              = level->bd_amoeba_slow_growth_rate * 10000;
4181   cave->amoeba_fast_growth_prob         = level->bd_amoeba_fast_growth_rate * 10000;
4182   cave->level_amoeba_2_threshold[0]     = level->bd_amoeba_2_threshold_too_big;
4183   cave->level_amoeba_2_time[0]          = level->bd_amoeba_2_slow_growth_time;
4184   cave->amoeba_2_growth_prob            = level->bd_amoeba_2_slow_growth_rate * 10000;
4185   cave->amoeba_2_fast_growth_prob       = level->bd_amoeba_2_fast_growth_rate * 10000;
4186
4187   cave->amoeba_too_big_effect           = LEVEL_TO_CAVE(level->bd_amoeba_content_too_big);
4188   cave->amoeba_enclosed_effect          = LEVEL_TO_CAVE(level->bd_amoeba_content_enclosed);
4189   cave->amoeba_2_too_big_effect         = LEVEL_TO_CAVE(level->bd_amoeba_2_content_too_big);
4190   cave->amoeba_2_enclosed_effect        = LEVEL_TO_CAVE(level->bd_amoeba_2_content_enclosed);
4191   cave->amoeba_2_explosion_effect       = LEVEL_TO_CAVE(level->bd_amoeba_2_content_exploding);
4192   cave->amoeba_2_looks_like             = LEVEL_TO_CAVE(level->bd_amoeba_2_content_looks_like);
4193
4194   cave->slime_predictable               = level->bd_slime_is_predictable;
4195   cave->slime_correct_random            = level->bd_slime_correct_random;
4196   cave->level_slime_permeability[0]     = level->bd_slime_permeability_rate * 10000;
4197   cave->level_slime_permeability_c64[0] = level->bd_slime_permeability_bits_c64;
4198   cave->level_slime_seed_c64[0]         = level->bd_slime_random_seed_c64;
4199   cave->level_rand[0]                   = level->bd_cave_random_seed_c64;
4200   cave->slime_eats_1                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_1);
4201   cave->slime_converts_1                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_1);
4202   cave->slime_eats_2                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_2);
4203   cave->slime_converts_2                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_2);
4204   cave->slime_eats_3                    = LEVEL_TO_CAVE(level->bd_slime_eats_element_3);
4205   cave->slime_converts_3                = LEVEL_TO_CAVE(level->bd_slime_converts_to_element_3);
4206
4207   cave->acid_eats_this                  = LEVEL_TO_CAVE(level->bd_acid_eats_element);
4208   cave->acid_spread_ratio               = level->bd_acid_spread_rate * 10000;
4209   cave->acid_turns_to                   = LEVEL_TO_CAVE(level->bd_acid_turns_to_element);
4210
4211   cave->biter_delay_frame               = level->bd_biter_move_delay;
4212   cave->biter_eat                       = LEVEL_TO_CAVE(level->bd_biter_eats_element);
4213
4214   cave->bladder_converts_by             = LEVEL_TO_CAVE(level->bd_bladder_converts_by_element);
4215
4216   cave->expanding_wall_changed          = level->bd_change_expanding_wall;
4217
4218   cave->replicators_active              = level->bd_replicators_active;
4219   cave->replicator_delay_frame          = level->bd_replicator_create_delay;
4220
4221   cave->conveyor_belts_active           = level->bd_conveyor_belts_active;
4222   cave->conveyor_belts_direction_changed= level->bd_conveyor_belts_changed;
4223
4224   cave->water_does_not_flow_down        = level->bd_water_cannot_flow_down;
4225
4226   cave->nut_turns_to_when_crushed       = LEVEL_TO_CAVE(level->bd_nut_content);
4227
4228   cave->pneumatic_hammer_frame          = level->bd_hammer_walls_break_delay;
4229   cave->hammered_walls_reappear         = level->bd_hammer_walls_reappear;
4230   cave->hammered_wall_reappear_frame    = level->bd_hammer_walls_reappear_delay;
4231
4232   cave->skeletons_needed_for_pot        = level->bd_num_skeletons_needed_for_pot;
4233   cave->skeletons_worth_diamonds        = level->bd_skeleton_worth_num_diamonds;
4234
4235   cave->expanding_wall_looks_like       = LEVEL_TO_CAVE(level->bd_expanding_wall_looks_like);
4236   cave->dirt_looks_like                 = LEVEL_TO_CAVE(level->bd_sand_looks_like);
4237
4238   cave->creatures_backwards                      = level->bd_creatures_start_backwards;
4239   cave->creatures_direction_auto_change_on_start = level->bd_creatures_turn_on_hatching;
4240   cave->creatures_direction_auto_change_time     = level->bd_creatures_auto_turn_delay;
4241
4242   // level name
4243   strncpy(cave->name, level->name, sizeof(GdString));
4244   cave->name[sizeof(GdString) - 1] = '\0';
4245
4246   // playfield elements
4247   for (x = 0; x < cave->w; x++)
4248     for (y = 0; y < cave->h; y++)
4249       cave->map[y][x] = LEVEL_TO_CAVE(level->field[x][y]);
4250 }
4251
4252 static void CopyNativeLevel_BD_to_RND(struct LevelInfo *level)
4253 {
4254   struct LevelInfo_BD *level_bd = level->native_bd_level;
4255   GdCave *cave = level_bd->cave;
4256   int bd_level_nr = level_bd->level_nr;
4257   int x, y;
4258
4259   level->fieldx = MIN(cave->w, MAX_LEV_FIELDX);
4260   level->fieldy = MIN(cave->h, MAX_LEV_FIELDY);
4261
4262   // level type
4263   level->bd_intermission                = cave->intermission;
4264
4265   // level settings
4266   level->time                           = cave->level_time[bd_level_nr];
4267   level->gems_needed                    = cave->level_diamonds[bd_level_nr];
4268
4269   // game timing
4270   level->bd_scheduling_type             = cave->scheduling;
4271   level->bd_pal_timing                  = cave->pal_timing;
4272   level->bd_cycle_delay_ms              = cave->level_speed[bd_level_nr];
4273   level->bd_cycle_delay_c64             = cave->level_ckdelay[bd_level_nr];
4274   level->bd_hatching_delay_cycles       = cave->level_hatching_delay_frame[bd_level_nr];
4275   level->bd_hatching_delay_seconds      = cave->level_hatching_delay_time[bd_level_nr];
4276
4277   // scores
4278   level->score[SC_TIME_BONUS]           = cave->level_timevalue[bd_level_nr];
4279   level->score[SC_EMERALD]              = cave->diamond_value;
4280   level->score[SC_DIAMOND_EXTRA]        = cave->extra_diamond_value;
4281
4282   // compatibility settings
4283   level->bd_line_shifting_borders       = cave->lineshift;
4284   level->bd_scan_first_and_last_row     = cave->border_scan_first_and_last;
4285   level->bd_short_explosions            = cave->short_explosions;
4286   level->bd_gravity_affects_all         = cave->gravity_affects_all;
4287
4288   // player properties
4289   level->bd_diagonal_movements          = cave->diagonal_movements;
4290   level->bd_topmost_player_active       = cave->active_is_first_found;
4291   level->bd_pushing_prob                = cave->pushing_stone_prob       / 10000;
4292   level->bd_pushing_prob_with_sweet     = cave->pushing_stone_prob_sweet / 10000;
4293   level->bd_push_mega_rock_with_sweet   = cave->mega_stones_pushable_with_sweet;
4294   level->bd_snap_element                = CAVE_TO_LEVEL(cave->snap_element);
4295
4296   // element properties
4297   level->bd_clock_extra_time            = cave->level_bonus_time[bd_level_nr];
4298   level->bd_voodoo_collects_diamonds    = cave->voodoo_collects_diamonds;
4299   level->bd_voodoo_hurt_kills_player    = cave->voodoo_any_hurt_kills_player;
4300   level->bd_voodoo_dies_by_rock         = cave->voodoo_dies_by_stone;
4301   level->bd_voodoo_vanish_by_explosion  = cave->voodoo_disappear_in_explosion;
4302   level->bd_voodoo_penalty_time         = cave->level_penalty_time[bd_level_nr];
4303   level->time_magic_wall                = cave->level_magic_wall_time[bd_level_nr];
4304   level->bd_magic_wall_wait_hatching    = cave->magic_timer_wait_for_hatching;
4305   level->bd_magic_wall_stops_amoeba     = cave->magic_wall_stops_amoeba;
4306
4307   level->bd_magic_wall_diamond_to       = CAVE_TO_LEVEL(cave->magic_diamond_to);
4308   level->bd_magic_wall_rock_to          = CAVE_TO_LEVEL(cave->magic_stone_to);
4309   level->bd_magic_wall_mega_rock_to     = CAVE_TO_LEVEL(cave->magic_mega_stone_to);
4310   level->bd_magic_wall_nut_to           = CAVE_TO_LEVEL(cave->magic_nut_to);
4311   level->bd_magic_wall_nitro_pack_to    = CAVE_TO_LEVEL(cave->magic_nitro_pack_to);
4312   level->bd_magic_wall_flying_diamond_to= CAVE_TO_LEVEL(cave->magic_flying_diamond_to);
4313   level->bd_magic_wall_flying_rock_to   = CAVE_TO_LEVEL(cave->magic_flying_stone_to);
4314
4315   level->bd_amoeba_wait_for_hatching    = cave->amoeba_timer_wait_for_hatching;
4316   level->bd_amoeba_start_immediately    = cave->amoeba_timer_started_immediately;
4317   level->bd_amoeba_2_explode_by_amoeba  = cave->amoeba_2_explodes_by_amoeba;
4318   level->bd_amoeba_threshold_too_big    = cave->level_amoeba_threshold[bd_level_nr];
4319   level->bd_amoeba_slow_growth_time     = cave->level_amoeba_time[bd_level_nr];
4320   level->bd_amoeba_slow_growth_rate     = cave->amoeba_growth_prob      / 10000;
4321   level->bd_amoeba_fast_growth_rate     = cave->amoeba_fast_growth_prob / 10000;
4322   level->bd_amoeba_2_threshold_too_big  = cave->level_amoeba_2_threshold[bd_level_nr];
4323   level->bd_amoeba_2_slow_growth_time   = cave->level_amoeba_2_time[bd_level_nr];
4324   level->bd_amoeba_2_slow_growth_rate   = cave->amoeba_2_growth_prob      / 10000;
4325   level->bd_amoeba_2_fast_growth_rate   = cave->amoeba_2_fast_growth_prob / 10000;
4326
4327   level->bd_amoeba_content_too_big      = CAVE_TO_LEVEL(cave->amoeba_too_big_effect);
4328   level->bd_amoeba_content_enclosed     = CAVE_TO_LEVEL(cave->amoeba_enclosed_effect);
4329   level->bd_amoeba_2_content_too_big    = CAVE_TO_LEVEL(cave->amoeba_2_too_big_effect);
4330   level->bd_amoeba_2_content_enclosed   = CAVE_TO_LEVEL(cave->amoeba_2_enclosed_effect);
4331   level->bd_amoeba_2_content_exploding  = CAVE_TO_LEVEL(cave->amoeba_2_explosion_effect);
4332   level->bd_amoeba_2_content_looks_like = CAVE_TO_LEVEL(cave->amoeba_2_looks_like);
4333
4334   level->bd_slime_is_predictable        = cave->slime_predictable;
4335   level->bd_slime_correct_random        = cave->slime_correct_random;
4336   level->bd_slime_permeability_rate     = cave->level_slime_permeability[bd_level_nr] / 10000;
4337   level->bd_slime_permeability_bits_c64 = cave->level_slime_permeability_c64[bd_level_nr];
4338   level->bd_slime_random_seed_c64       = cave->level_slime_seed_c64[bd_level_nr];
4339   level->bd_cave_random_seed_c64        = cave->level_rand[bd_level_nr];
4340   level->bd_slime_eats_element_1        = CAVE_TO_LEVEL(cave->slime_eats_1);
4341   level->bd_slime_converts_to_element_1 = CAVE_TO_LEVEL(cave->slime_converts_1);
4342   level->bd_slime_eats_element_2        = CAVE_TO_LEVEL(cave->slime_eats_2);
4343   level->bd_slime_converts_to_element_2 = CAVE_TO_LEVEL(cave->slime_converts_2);
4344   level->bd_slime_eats_element_3        = CAVE_TO_LEVEL(cave->slime_eats_3);
4345   level->bd_slime_converts_to_element_3 = CAVE_TO_LEVEL(cave->slime_converts_3);
4346
4347   level->bd_acid_eats_element           = CAVE_TO_LEVEL(cave->acid_eats_this);
4348   level->bd_acid_spread_rate            = cave->acid_spread_ratio / 10000;
4349   level->bd_acid_turns_to_element       = CAVE_TO_LEVEL(cave->acid_turns_to);
4350
4351   level->bd_biter_move_delay            = cave->biter_delay_frame;
4352   level->bd_biter_eats_element          = CAVE_TO_LEVEL(cave->biter_eat);
4353
4354   level->bd_bladder_converts_by_element = CAVE_TO_LEVEL(cave->bladder_converts_by);
4355
4356   level->bd_change_expanding_wall       = cave->expanding_wall_changed;
4357
4358   level->bd_replicators_active          = cave->replicators_active;
4359   level->bd_replicator_create_delay     = cave->replicator_delay_frame;
4360
4361   level->bd_conveyor_belts_active       = cave->conveyor_belts_active;
4362   level->bd_conveyor_belts_changed      = cave->conveyor_belts_direction_changed;
4363
4364   level->bd_water_cannot_flow_down      = cave->water_does_not_flow_down;
4365
4366   level->bd_nut_content                 = CAVE_TO_LEVEL(cave->nut_turns_to_when_crushed);
4367
4368   level->bd_hammer_walls_break_delay    = cave->pneumatic_hammer_frame;
4369   level->bd_hammer_walls_reappear       = cave->hammered_walls_reappear;
4370   level->bd_hammer_walls_reappear_delay = cave->hammered_wall_reappear_frame;
4371
4372   level->bd_num_skeletons_needed_for_pot= cave->skeletons_needed_for_pot;
4373   level->bd_skeleton_worth_num_diamonds = cave->skeletons_worth_diamonds;
4374
4375   level->bd_expanding_wall_looks_like   = CAVE_TO_LEVEL(cave->expanding_wall_looks_like);
4376   level->bd_sand_looks_like             = CAVE_TO_LEVEL(cave->dirt_looks_like);
4377
4378   level->bd_creatures_start_backwards   = cave->creatures_backwards;
4379   level->bd_creatures_turn_on_hatching  = cave->creatures_direction_auto_change_on_start;
4380   level->bd_creatures_auto_turn_delay   = cave->creatures_direction_auto_change_time;
4381
4382   // level name
4383   char *cave_name = getStringPrint("%s / %d", cave->name, bd_level_nr + 1);
4384
4385   strncpy(level->name, cave_name, MAX_LEVEL_NAME_LEN);
4386   level->name[MAX_LEVEL_NAME_LEN] = '\0';
4387
4388   // playfield elements
4389   for (x = 0; x < level->fieldx; x++)
4390     for (y = 0; y < level->fieldy; y++)
4391       level->field[x][y] = CAVE_TO_LEVEL(cave->map[y][x]);
4392
4393   checked_free(cave_name);
4394 }
4395
4396 static void setTapeInfoToDefaults(void);
4397
4398 static void CopyNativeTape_BD_to_RND(struct LevelInfo *level)
4399 {
4400   struct LevelInfo_BD *level_bd = level->native_bd_level;
4401   GdCave *cave = level_bd->cave;
4402   GdReplay *replay = level_bd->replay;
4403   int i;
4404
4405   if (replay == NULL)
4406     return;
4407
4408   // always start with reliable default values
4409   setTapeInfoToDefaults();
4410
4411   tape.level_nr = level_nr;             // (currently not used)
4412   tape.random_seed = replay->seed;
4413
4414   TapeSetDateFromIsoDateString(replay->date);
4415
4416   tape.counter = 0;
4417   tape.pos[tape.counter].delay = 0;
4418
4419   tape.bd_replay = TRUE;
4420
4421   // all time calculations only used to display approximate tape time
4422   int cave_speed = cave->speed;
4423   int milliseconds_game = 0;
4424   int milliseconds_elapsed = 20;
4425
4426   for (i = 0; i < replay->movements->len; i++)
4427   {
4428     int replay_action = replay->movements->data[i];
4429     int tape_action = map_action_BD_to_RND(replay_action);
4430     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4431     boolean success = 0;
4432
4433     while (1)
4434     {
4435       success = TapeAddAction(action);
4436
4437       milliseconds_game += milliseconds_elapsed;
4438
4439       if (milliseconds_game >= cave_speed)
4440       {
4441         milliseconds_game -= cave_speed;
4442
4443         break;
4444       }
4445     }
4446
4447     tape.counter++;
4448     tape.pos[tape.counter].delay = 0;
4449     tape.pos[tape.counter].action[0] = 0;
4450
4451     if (!success)
4452     {
4453       Warn("BD replay truncated: size exceeds maximum tape size %d", MAX_TAPE_LEN);
4454
4455       break;
4456     }
4457   }
4458
4459   TapeHaltRecording();
4460 }
4461
4462
4463 // ----------------------------------------------------------------------------
4464 // functions for loading EM level
4465 // ----------------------------------------------------------------------------
4466
4467 static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
4468 {
4469   static int ball_xy[8][2] =
4470   {
4471     { 0, 0 },
4472     { 1, 0 },
4473     { 2, 0 },
4474     { 0, 1 },
4475     { 2, 1 },
4476     { 0, 2 },
4477     { 1, 2 },
4478     { 2, 2 },
4479   };
4480   struct LevelInfo_EM *level_em = level->native_em_level;
4481   struct CAVE *cav = level_em->cav;
4482   int i, j, x, y;
4483
4484   cav->width  = MIN(level->fieldx, MAX_PLAYFIELD_WIDTH);
4485   cav->height = MIN(level->fieldy, MAX_PLAYFIELD_HEIGHT);
4486
4487   cav->time_seconds     = level->time;
4488   cav->gems_needed      = level->gems_needed;
4489
4490   cav->emerald_score    = level->score[SC_EMERALD];
4491   cav->diamond_score    = level->score[SC_DIAMOND];
4492   cav->alien_score      = level->score[SC_ROBOT];
4493   cav->tank_score       = level->score[SC_SPACESHIP];
4494   cav->bug_score        = level->score[SC_BUG];
4495   cav->eater_score      = level->score[SC_YAMYAM];
4496   cav->nut_score        = level->score[SC_NUT];
4497   cav->dynamite_score   = level->score[SC_DYNAMITE];
4498   cav->key_score        = level->score[SC_KEY];
4499   cav->exit_score       = level->score[SC_TIME_BONUS];
4500
4501   cav->num_eater_arrays = level->num_yamyam_contents;
4502
4503   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4504     for (y = 0; y < 3; y++)
4505       for (x = 0; x < 3; x++)
4506         cav->eater_array[i][y * 3 + x] =
4507           map_element_RND_to_EM_cave(level->yamyam_content[i].e[x][y]);
4508
4509   cav->amoeba_time              = level->amoeba_speed;
4510   cav->wonderwall_time          = level->time_magic_wall;
4511   cav->wheel_time               = level->time_wheel;
4512
4513   cav->android_move_time        = level->android_move_time;
4514   cav->android_clone_time       = level->android_clone_time;
4515   cav->ball_random              = level->ball_random;
4516   cav->ball_active              = level->ball_active_initial;
4517   cav->ball_time                = level->ball_time;
4518   cav->num_ball_arrays          = level->num_ball_contents;
4519
4520   cav->lenses_score             = level->lenses_score;
4521   cav->magnify_score            = level->magnify_score;
4522   cav->slurp_score              = level->slurp_score;
4523
4524   cav->lenses_time              = level->lenses_time;
4525   cav->magnify_time             = level->magnify_time;
4526
4527   cav->wind_time = 9999;
4528   cav->wind_direction =
4529     map_direction_RND_to_EM(level->wind_direction_initial);
4530
4531   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4532     for (j = 0; j < 8; j++)
4533       cav->ball_array[i][j] =
4534         map_element_RND_to_EM_cave(level->ball_content[i].
4535                                    e[ball_xy[j][0]][ball_xy[j][1]]);
4536
4537   map_android_clone_elements_RND_to_EM(level);
4538
4539   // first fill the complete playfield with the empty space element
4540   for (y = 0; y < EM_MAX_CAVE_HEIGHT; y++)
4541     for (x = 0; x < EM_MAX_CAVE_WIDTH; x++)
4542       cav->cave[x][y] = Cblank;
4543
4544   // then copy the real level contents from level file into the playfield
4545   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4546   {
4547     int new_element = map_element_RND_to_EM_cave(level->field[x][y]);
4548
4549     if (level->field[x][y] == EL_AMOEBA_DEAD)
4550       new_element = map_element_RND_to_EM_cave(EL_AMOEBA_WET);
4551
4552     cav->cave[x][y] = new_element;
4553   }
4554
4555   for (i = 0; i < MAX_PLAYERS; i++)
4556   {
4557     cav->player_x[i] = -1;
4558     cav->player_y[i] = -1;
4559   }
4560
4561   // initialize player positions and delete players from the playfield
4562   for (y = 0; y < cav->height; y++) for (x = 0; x < cav->width; x++)
4563   {
4564     if (IS_PLAYER_ELEMENT(level->field[x][y]))
4565     {
4566       int player_nr = GET_PLAYER_NR(level->field[x][y]);
4567
4568       cav->player_x[player_nr] = x;
4569       cav->player_y[player_nr] = y;
4570
4571       cav->cave[x][y] = map_element_RND_to_EM_cave(EL_EMPTY);
4572     }
4573   }
4574 }
4575
4576 static void CopyNativeLevel_EM_to_RND(struct LevelInfo *level)
4577 {
4578   static int ball_xy[8][2] =
4579   {
4580     { 0, 0 },
4581     { 1, 0 },
4582     { 2, 0 },
4583     { 0, 1 },
4584     { 2, 1 },
4585     { 0, 2 },
4586     { 1, 2 },
4587     { 2, 2 },
4588   };
4589   struct LevelInfo_EM *level_em = level->native_em_level;
4590   struct CAVE *cav = level_em->cav;
4591   int i, j, x, y;
4592
4593   level->fieldx = MIN(cav->width,  MAX_LEV_FIELDX);
4594   level->fieldy = MIN(cav->height, MAX_LEV_FIELDY);
4595
4596   level->time        = cav->time_seconds;
4597   level->gems_needed = cav->gems_needed;
4598
4599   sprintf(level->name, "Level %d", level->file_info.nr);
4600
4601   level->score[SC_EMERALD]      = cav->emerald_score;
4602   level->score[SC_DIAMOND]      = cav->diamond_score;
4603   level->score[SC_ROBOT]        = cav->alien_score;
4604   level->score[SC_SPACESHIP]    = cav->tank_score;
4605   level->score[SC_BUG]          = cav->bug_score;
4606   level->score[SC_YAMYAM]       = cav->eater_score;
4607   level->score[SC_NUT]          = cav->nut_score;
4608   level->score[SC_DYNAMITE]     = cav->dynamite_score;
4609   level->score[SC_KEY]          = cav->key_score;
4610   level->score[SC_TIME_BONUS]   = cav->exit_score;
4611
4612   level->num_yamyam_contents    = cav->num_eater_arrays;
4613
4614   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4615     for (y = 0; y < 3; y++)
4616       for (x = 0; x < 3; x++)
4617         level->yamyam_content[i].e[x][y] =
4618           map_element_EM_to_RND_cave(cav->eater_array[i][y * 3 + x]);
4619
4620   level->amoeba_speed           = cav->amoeba_time;
4621   level->time_magic_wall        = cav->wonderwall_time;
4622   level->time_wheel             = cav->wheel_time;
4623
4624   level->android_move_time      = cav->android_move_time;
4625   level->android_clone_time     = cav->android_clone_time;
4626   level->ball_random            = cav->ball_random;
4627   level->ball_active_initial    = cav->ball_active;
4628   level->ball_time              = cav->ball_time;
4629   level->num_ball_contents      = cav->num_ball_arrays;
4630
4631   level->lenses_score           = cav->lenses_score;
4632   level->magnify_score          = cav->magnify_score;
4633   level->slurp_score            = cav->slurp_score;
4634
4635   level->lenses_time            = cav->lenses_time;
4636   level->magnify_time           = cav->magnify_time;
4637
4638   level->wind_direction_initial =
4639     map_direction_EM_to_RND(cav->wind_direction);
4640
4641   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
4642     for (j = 0; j < 8; j++)
4643       level->ball_content[i].e[ball_xy[j][0]][ball_xy[j][1]] =
4644         map_element_EM_to_RND_cave(cav->ball_array[i][j]);
4645
4646   map_android_clone_elements_EM_to_RND(level);
4647
4648   // convert the playfield (some elements need special treatment)
4649   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
4650   {
4651     int new_element = map_element_EM_to_RND_cave(cav->cave[x][y]);
4652
4653     if (new_element == EL_AMOEBA_WET && level->amoeba_speed == 0)
4654       new_element = EL_AMOEBA_DEAD;
4655
4656     level->field[x][y] = new_element;
4657   }
4658
4659   for (i = 0; i < MAX_PLAYERS; i++)
4660   {
4661     // in case of all players set to the same field, use the first player
4662     int nr = MAX_PLAYERS - i - 1;
4663     int jx = cav->player_x[nr];
4664     int jy = cav->player_y[nr];
4665
4666     if (jx != -1 && jy != -1)
4667       level->field[jx][jy] = EL_PLAYER_1 + nr;
4668   }
4669
4670   // time score is counted for each 10 seconds left in Emerald Mine levels
4671   level->time_score_base = 10;
4672 }
4673
4674
4675 // ----------------------------------------------------------------------------
4676 // functions for loading SP level
4677 // ----------------------------------------------------------------------------
4678
4679 static void CopyNativeLevel_RND_to_SP(struct LevelInfo *level)
4680 {
4681   struct LevelInfo_SP *level_sp = level->native_sp_level;
4682   LevelInfoType *header = &level_sp->header;
4683   int i, x, y;
4684
4685   level_sp->width  = level->fieldx;
4686   level_sp->height = level->fieldy;
4687
4688   for (x = 0; x < level->fieldx; x++)
4689     for (y = 0; y < level->fieldy; y++)
4690       level_sp->playfield[x][y] = map_element_RND_to_SP(level->field[x][y]);
4691
4692   header->InitialGravity = (level->initial_player_gravity[0] ? 1 : 0);
4693
4694   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4695     header->LevelTitle[i] = level->name[i];
4696   // !!! NO STRING TERMINATION IN SUPAPLEX VB CODE YET -- FIX THIS !!!
4697
4698   header->InfotronsNeeded = level->gems_needed;
4699
4700   header->SpecialPortCount = 0;
4701
4702   for (x = 0; x < level->fieldx; x++) for (y = 0; y < level->fieldy; y++)
4703   {
4704     boolean gravity_port_found = FALSE;
4705     boolean gravity_port_valid = FALSE;
4706     int gravity_port_flag;
4707     int gravity_port_base_element;
4708     int element = level->field[x][y];
4709
4710     if (element >= EL_SP_GRAVITY_ON_PORT_RIGHT &&
4711         element <= EL_SP_GRAVITY_ON_PORT_UP)
4712     {
4713       gravity_port_found = TRUE;
4714       gravity_port_valid = TRUE;
4715       gravity_port_flag = 1;
4716       gravity_port_base_element = EL_SP_GRAVITY_ON_PORT_RIGHT;
4717     }
4718     else if (element >= EL_SP_GRAVITY_OFF_PORT_RIGHT &&
4719              element <= EL_SP_GRAVITY_OFF_PORT_UP)
4720     {
4721       gravity_port_found = TRUE;
4722       gravity_port_valid = TRUE;
4723       gravity_port_flag = 0;
4724       gravity_port_base_element = EL_SP_GRAVITY_OFF_PORT_RIGHT;
4725     }
4726     else if (element >= EL_SP_GRAVITY_PORT_RIGHT &&
4727              element <= EL_SP_GRAVITY_PORT_UP)
4728     {
4729       // change R'n'D style gravity inverting special port to normal port
4730       // (there are no gravity inverting ports in native Supaplex engine)
4731
4732       gravity_port_found = TRUE;
4733       gravity_port_valid = FALSE;
4734       gravity_port_base_element = EL_SP_GRAVITY_PORT_RIGHT;
4735     }
4736
4737     if (gravity_port_found)
4738     {
4739       if (gravity_port_valid &&
4740           header->SpecialPortCount < SP_MAX_SPECIAL_PORTS)
4741       {
4742         SpecialPortType *port = &header->SpecialPort[header->SpecialPortCount];
4743
4744         port->PortLocation = (y * level->fieldx + x) * 2;
4745         port->Gravity = gravity_port_flag;
4746
4747         element += EL_SP_GRAVITY_PORT_RIGHT - gravity_port_base_element;
4748
4749         header->SpecialPortCount++;
4750       }
4751       else
4752       {
4753         // change special gravity port to normal port
4754
4755         element += EL_SP_PORT_RIGHT - gravity_port_base_element;
4756       }
4757
4758       level_sp->playfield[x][y] = element - EL_SP_START;
4759     }
4760   }
4761 }
4762
4763 static void CopyNativeLevel_SP_to_RND(struct LevelInfo *level)
4764 {
4765   struct LevelInfo_SP *level_sp = level->native_sp_level;
4766   LevelInfoType *header = &level_sp->header;
4767   boolean num_invalid_elements = 0;
4768   int i, j, x, y;
4769
4770   level->fieldx = level_sp->width;
4771   level->fieldy = level_sp->height;
4772
4773   for (x = 0; x < level->fieldx; x++)
4774   {
4775     for (y = 0; y < level->fieldy; y++)
4776     {
4777       int element_old = level_sp->playfield[x][y];
4778       int element_new = getMappedElement(map_element_SP_to_RND(element_old));
4779
4780       if (element_new == EL_UNKNOWN)
4781       {
4782         num_invalid_elements++;
4783
4784         Debug("level:native:SP", "invalid element %d at position %d, %d",
4785               element_old, x, y);
4786       }
4787
4788       level->field[x][y] = element_new;
4789     }
4790   }
4791
4792   if (num_invalid_elements > 0)
4793     Warn("found %d invalid elements%s", num_invalid_elements,
4794          (!options.debug ? " (use '--debug' for more details)" : ""));
4795
4796   for (i = 0; i < MAX_PLAYERS; i++)
4797     level->initial_player_gravity[i] =
4798       (header->InitialGravity == 1 ? TRUE : FALSE);
4799
4800   // skip leading spaces
4801   for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
4802     if (header->LevelTitle[i] != ' ')
4803       break;
4804
4805   // copy level title
4806   for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
4807     level->name[j] = header->LevelTitle[i];
4808   level->name[j] = '\0';
4809
4810   // cut trailing spaces
4811   for (; j > 0; j--)
4812     if (level->name[j - 1] == ' ' && level->name[j] == '\0')
4813       level->name[j - 1] = '\0';
4814
4815   level->gems_needed = header->InfotronsNeeded;
4816
4817   for (i = 0; i < header->SpecialPortCount; i++)
4818   {
4819     SpecialPortType *port = &header->SpecialPort[i];
4820     int port_location = port->PortLocation;
4821     int gravity = port->Gravity;
4822     int port_x, port_y, port_element;
4823
4824     port_x = (port_location / 2) % level->fieldx;
4825     port_y = (port_location / 2) / level->fieldx;
4826
4827     if (port_x < 0 || port_x >= level->fieldx ||
4828         port_y < 0 || port_y >= level->fieldy)
4829     {
4830       Warn("special port position (%d, %d) out of bounds", port_x, port_y);
4831
4832       continue;
4833     }
4834
4835     port_element = level->field[port_x][port_y];
4836
4837     if (port_element < EL_SP_GRAVITY_PORT_RIGHT ||
4838         port_element > EL_SP_GRAVITY_PORT_UP)
4839     {
4840       Warn("no special port at position (%d, %d)", port_x, port_y);
4841
4842       continue;
4843     }
4844
4845     // change previous (wrong) gravity inverting special port to either
4846     // gravity enabling special port or gravity disabling special port
4847     level->field[port_x][port_y] +=
4848       (gravity == 1 ? EL_SP_GRAVITY_ON_PORT_RIGHT :
4849        EL_SP_GRAVITY_OFF_PORT_RIGHT) - EL_SP_GRAVITY_PORT_RIGHT;
4850   }
4851
4852   // change special gravity ports without database entries to normal ports
4853   for (x = 0; x < level->fieldx; x++)
4854     for (y = 0; y < level->fieldy; y++)
4855       if (level->field[x][y] >= EL_SP_GRAVITY_PORT_RIGHT &&
4856           level->field[x][y] <= EL_SP_GRAVITY_PORT_UP)
4857         level->field[x][y] += EL_SP_PORT_RIGHT - EL_SP_GRAVITY_PORT_RIGHT;
4858
4859   level->time = 0;                      // no time limit
4860   level->amoeba_speed = 0;
4861   level->time_magic_wall = 0;
4862   level->time_wheel = 0;
4863   level->amoeba_content = EL_EMPTY;
4864
4865   // original Supaplex does not use score values -- rate by playing time
4866   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
4867     level->score[i] = 0;
4868
4869   level->rate_time_over_score = TRUE;
4870
4871   // there are no yamyams in supaplex levels
4872   for (i = 0; i < level->num_yamyam_contents; i++)
4873     for (x = 0; x < 3; x++)
4874       for (y = 0; y < 3; y++)
4875         level->yamyam_content[i].e[x][y] = EL_EMPTY;
4876 }
4877
4878 static void CopyNativeTape_RND_to_SP(struct LevelInfo *level)
4879 {
4880   struct LevelInfo_SP *level_sp = level->native_sp_level;
4881   struct DemoInfo_SP *demo = &level_sp->demo;
4882   int i, j;
4883
4884   // always start with reliable default values
4885   demo->is_available = FALSE;
4886   demo->length = 0;
4887
4888   if (TAPE_IS_EMPTY(tape))
4889     return;
4890
4891   demo->level_nr = tape.level_nr;       // (currently not used)
4892
4893   level_sp->header.DemoRandomSeed = tape.random_seed;
4894
4895   demo->length = 0;
4896
4897   for (i = 0; i < tape.length; i++)
4898   {
4899     int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
4900     int demo_repeat = tape.pos[i].delay;
4901     int demo_entries = (demo_repeat + 15) / 16;
4902
4903     if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
4904     {
4905       Warn("tape truncated: size exceeds maximum SP demo size %d",
4906            SP_MAX_TAPE_LEN);
4907
4908       break;
4909     }
4910
4911     for (j = 0; j < demo_repeat / 16; j++)
4912       demo->data[demo->length++] = 0xf0 | demo_action;
4913
4914     if (demo_repeat % 16)
4915       demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
4916   }
4917
4918   demo->is_available = TRUE;
4919 }
4920
4921 static void CopyNativeTape_SP_to_RND(struct LevelInfo *level)
4922 {
4923   struct LevelInfo_SP *level_sp = level->native_sp_level;
4924   struct DemoInfo_SP *demo = &level_sp->demo;
4925   char *filename = level->file_info.filename;
4926   int i;
4927
4928   // always start with reliable default values
4929   setTapeInfoToDefaults();
4930
4931   if (!demo->is_available)
4932     return;
4933
4934   tape.level_nr = demo->level_nr;       // (currently not used)
4935   tape.random_seed = level_sp->header.DemoRandomSeed;
4936
4937   TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
4938
4939   tape.counter = 0;
4940   tape.pos[tape.counter].delay = 0;
4941
4942   for (i = 0; i < demo->length; i++)
4943   {
4944     int demo_action = demo->data[i] & 0x0f;
4945     int demo_repeat = (demo->data[i] & 0xf0) >> 4;
4946     int tape_action = map_key_SP_to_RND(demo_action);
4947     int tape_repeat = demo_repeat + 1;
4948     byte action[MAX_TAPE_ACTIONS] = { tape_action };
4949     boolean success = 0;
4950     int j;
4951
4952     for (j = 0; j < tape_repeat; j++)
4953       success = TapeAddAction(action);
4954
4955     if (!success)
4956     {
4957       Warn("SP demo truncated: size exceeds maximum tape size %d",
4958            MAX_TAPE_LEN);
4959
4960       break;
4961     }
4962   }
4963
4964   TapeHaltRecording();
4965 }
4966
4967
4968 // ----------------------------------------------------------------------------
4969 // functions for loading MM level
4970 // ----------------------------------------------------------------------------
4971
4972 static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
4973 {
4974   struct LevelInfo_MM *level_mm = level->native_mm_level;
4975   int i, x, y;
4976
4977   level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
4978   level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
4979
4980   level_mm->time = level->time;
4981   level_mm->kettles_needed = level->gems_needed;
4982   level_mm->auto_count_kettles = level->auto_count_gems;
4983
4984   level_mm->mm_laser_red   = level->mm_laser_red;
4985   level_mm->mm_laser_green = level->mm_laser_green;
4986   level_mm->mm_laser_blue  = level->mm_laser_blue;
4987
4988   level_mm->df_laser_red   = level->df_laser_red;
4989   level_mm->df_laser_green = level->df_laser_green;
4990   level_mm->df_laser_blue  = level->df_laser_blue;
4991
4992   strcpy(level_mm->name, level->name);
4993   strcpy(level_mm->author, level->author);
4994
4995   level_mm->score[SC_EMERALD]    = level->score[SC_EMERALD];
4996   level_mm->score[SC_PACMAN]     = level->score[SC_PACMAN];
4997   level_mm->score[SC_KEY]        = level->score[SC_KEY];
4998   level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
4999   level_mm->score[SC_ELEM_BONUS] = level->score[SC_ELEM_BONUS];
5000
5001   level_mm->amoeba_speed = level->amoeba_speed;
5002   level_mm->time_fuse    = level->mm_time_fuse;
5003   level_mm->time_bomb    = level->mm_time_bomb;
5004   level_mm->time_ball    = level->mm_time_ball;
5005   level_mm->time_block   = level->mm_time_block;
5006
5007   level_mm->num_ball_contents = level->num_mm_ball_contents;
5008   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
5009   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
5010   level_mm->explode_ball = level->explode_mm_ball;
5011
5012   for (i = 0; i < level->num_mm_ball_contents; i++)
5013     level_mm->ball_content[i] =
5014       map_element_RND_to_MM(level->mm_ball_content[i]);
5015
5016   for (x = 0; x < level->fieldx; x++)
5017     for (y = 0; y < level->fieldy; y++)
5018       Ur[x][y] =
5019         level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
5020 }
5021
5022 static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
5023 {
5024   struct LevelInfo_MM *level_mm = level->native_mm_level;
5025   int i, x, y;
5026
5027   level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
5028   level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
5029
5030   level->time = level_mm->time;
5031   level->gems_needed = level_mm->kettles_needed;
5032   level->auto_count_gems = level_mm->auto_count_kettles;
5033
5034   level->mm_laser_red   = level_mm->mm_laser_red;
5035   level->mm_laser_green = level_mm->mm_laser_green;
5036   level->mm_laser_blue  = level_mm->mm_laser_blue;
5037
5038   level->df_laser_red   = level_mm->df_laser_red;
5039   level->df_laser_green = level_mm->df_laser_green;
5040   level->df_laser_blue  = level_mm->df_laser_blue;
5041
5042   strcpy(level->name, level_mm->name);
5043
5044   // only overwrite author from 'levelinfo.conf' if author defined in level
5045   if (!strEqual(level_mm->author, ANONYMOUS_NAME))
5046     strcpy(level->author, level_mm->author);
5047
5048   level->score[SC_EMERALD]    = level_mm->score[SC_EMERALD];
5049   level->score[SC_PACMAN]     = level_mm->score[SC_PACMAN];
5050   level->score[SC_KEY]        = level_mm->score[SC_KEY];
5051   level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
5052   level->score[SC_ELEM_BONUS] = level_mm->score[SC_ELEM_BONUS];
5053
5054   level->amoeba_speed  = level_mm->amoeba_speed;
5055   level->mm_time_fuse  = level_mm->time_fuse;
5056   level->mm_time_bomb  = level_mm->time_bomb;
5057   level->mm_time_ball  = level_mm->time_ball;
5058   level->mm_time_block = level_mm->time_block;
5059
5060   level->num_mm_ball_contents = level_mm->num_ball_contents;
5061   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
5062   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
5063   level->explode_mm_ball = level_mm->explode_ball;
5064
5065   for (i = 0; i < level->num_mm_ball_contents; i++)
5066     level->mm_ball_content[i] =
5067       map_element_MM_to_RND(level_mm->ball_content[i]);
5068
5069   for (x = 0; x < level->fieldx; x++)
5070     for (y = 0; y < level->fieldy; y++)
5071       level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
5072 }
5073
5074
5075 // ----------------------------------------------------------------------------
5076 // functions for loading DC level
5077 // ----------------------------------------------------------------------------
5078
5079 #define DC_LEVEL_HEADER_SIZE            344
5080
5081 static unsigned short getDecodedWord_DC(unsigned short data_encoded,
5082                                         boolean init)
5083 {
5084   static int last_data_encoded;
5085   static int offset1;
5086   static int offset2;
5087   int diff;
5088   int diff_hi, diff_lo;
5089   int data_hi, data_lo;
5090   unsigned short data_decoded;
5091
5092   if (init)
5093   {
5094     last_data_encoded = 0;
5095     offset1 = -1;
5096     offset2 = 0;
5097
5098     return 0;
5099   }
5100
5101   diff = data_encoded - last_data_encoded;
5102   diff_hi = diff & ~0xff;
5103   diff_lo = diff &  0xff;
5104
5105   offset2 += diff_lo;
5106
5107   data_hi = diff_hi - (offset1 << 8) + (offset2 & 0xff00);
5108   data_lo = (diff_lo + (data_hi >> 16)) & 0x00ff;
5109   data_hi = data_hi & 0xff00;
5110
5111   data_decoded = data_hi | data_lo;
5112
5113   last_data_encoded = data_encoded;
5114
5115   offset1 = (offset1 + 1) % 31;
5116   offset2 = offset2 & 0xff;
5117
5118   return data_decoded;
5119 }
5120
5121 static int getMappedElement_DC(int element)
5122 {
5123   switch (element)
5124   {
5125     case 0x0000:
5126       element = EL_ROCK;
5127       break;
5128
5129       // 0x0117 - 0x036e: (?)
5130       // EL_DIAMOND
5131
5132       // 0x042d - 0x0684: (?)
5133       // EL_EMERALD
5134
5135     case 0x06f1:
5136       element = EL_NUT;
5137       break;
5138
5139     case 0x074c:
5140       element = EL_BOMB;
5141       break;
5142
5143     case 0x07a4:
5144       element = EL_PEARL;
5145       break;
5146
5147     case 0x0823:
5148       element = EL_CRYSTAL;
5149       break;
5150
5151     case 0x0e77:        // quicksand (boulder)
5152       element = EL_QUICKSAND_FAST_FULL;
5153       break;
5154
5155     case 0x0e99:        // slow quicksand (boulder)
5156       element = EL_QUICKSAND_FULL;
5157       break;
5158
5159     case 0x0ed2:
5160       element = EL_EM_EXIT_OPEN;
5161       break;
5162
5163     case 0x0ee3:
5164       element = EL_EM_EXIT_CLOSED;
5165       break;
5166
5167     case 0x0eeb:
5168       element = EL_EM_STEEL_EXIT_OPEN;
5169       break;
5170
5171     case 0x0efc:
5172       element = EL_EM_STEEL_EXIT_CLOSED;
5173       break;
5174
5175     case 0x0f4f:        // dynamite (lit 1)
5176       element = EL_EM_DYNAMITE_ACTIVE;
5177       break;
5178
5179     case 0x0f57:        // dynamite (lit 2)
5180       element = EL_EM_DYNAMITE_ACTIVE;
5181       break;
5182
5183     case 0x0f5f:        // dynamite (lit 3)
5184       element = EL_EM_DYNAMITE_ACTIVE;
5185       break;
5186
5187     case 0x0f67:        // dynamite (lit 4)
5188       element = EL_EM_DYNAMITE_ACTIVE;
5189       break;
5190
5191     case 0x0f81:
5192     case 0x0f82:
5193     case 0x0f83:
5194     case 0x0f84:
5195       element = EL_AMOEBA_WET;
5196       break;
5197
5198     case 0x0f85:
5199       element = EL_AMOEBA_DROP;
5200       break;
5201
5202     case 0x0fb9:
5203       element = EL_DC_MAGIC_WALL;
5204       break;
5205
5206     case 0x0fd0:
5207       element = EL_SPACESHIP_UP;
5208       break;
5209
5210     case 0x0fd9:
5211       element = EL_SPACESHIP_DOWN;
5212       break;
5213
5214     case 0x0ff1:
5215       element = EL_SPACESHIP_LEFT;
5216       break;
5217
5218     case 0x0ff9:
5219       element = EL_SPACESHIP_RIGHT;
5220       break;
5221
5222     case 0x1057:
5223       element = EL_BUG_UP;
5224       break;
5225
5226     case 0x1060:
5227       element = EL_BUG_DOWN;
5228       break;
5229
5230     case 0x1078:
5231       element = EL_BUG_LEFT;
5232       break;
5233
5234     case 0x1080:
5235       element = EL_BUG_RIGHT;
5236       break;
5237
5238     case 0x10de:
5239       element = EL_MOLE_UP;
5240       break;
5241
5242     case 0x10e7:
5243       element = EL_MOLE_DOWN;
5244       break;
5245
5246     case 0x10ff:
5247       element = EL_MOLE_LEFT;
5248       break;
5249
5250     case 0x1107:
5251       element = EL_MOLE_RIGHT;
5252       break;
5253
5254     case 0x11c0:
5255       element = EL_ROBOT;
5256       break;
5257
5258     case 0x13f5:
5259       element = EL_YAMYAM_UP;
5260       break;
5261
5262     case 0x1425:
5263       element = EL_SWITCHGATE_OPEN;
5264       break;
5265
5266     case 0x1426:
5267       element = EL_SWITCHGATE_CLOSED;
5268       break;
5269
5270     case 0x1437:
5271       element = EL_DC_SWITCHGATE_SWITCH_UP;
5272       break;
5273
5274     case 0x143a:
5275       element = EL_TIMEGATE_CLOSED;
5276       break;
5277
5278     case 0x144c:        // conveyor belt switch (green)
5279       element = EL_CONVEYOR_BELT_3_SWITCH_MIDDLE;
5280       break;
5281
5282     case 0x144f:        // conveyor belt switch (red)
5283       element = EL_CONVEYOR_BELT_1_SWITCH_MIDDLE;
5284       break;
5285
5286     case 0x1452:        // conveyor belt switch (blue)
5287       element = EL_CONVEYOR_BELT_4_SWITCH_MIDDLE;
5288       break;
5289
5290     case 0x145b:
5291       element = EL_CONVEYOR_BELT_3_MIDDLE;
5292       break;
5293
5294     case 0x1463:
5295       element = EL_CONVEYOR_BELT_3_LEFT;
5296       break;
5297
5298     case 0x146b:
5299       element = EL_CONVEYOR_BELT_3_RIGHT;
5300       break;
5301
5302     case 0x1473:
5303       element = EL_CONVEYOR_BELT_1_MIDDLE;
5304       break;
5305
5306     case 0x147b:
5307       element = EL_CONVEYOR_BELT_1_LEFT;
5308       break;
5309
5310     case 0x1483:
5311       element = EL_CONVEYOR_BELT_1_RIGHT;
5312       break;
5313
5314     case 0x148b:
5315       element = EL_CONVEYOR_BELT_4_MIDDLE;
5316       break;
5317
5318     case 0x1493:
5319       element = EL_CONVEYOR_BELT_4_LEFT;
5320       break;
5321
5322     case 0x149b:
5323       element = EL_CONVEYOR_BELT_4_RIGHT;
5324       break;
5325
5326     case 0x14ac:
5327       element = EL_EXPANDABLE_WALL_HORIZONTAL;
5328       break;
5329
5330     case 0x14bd:
5331       element = EL_EXPANDABLE_WALL_VERTICAL;
5332       break;
5333
5334     case 0x14c6:
5335       element = EL_EXPANDABLE_WALL_ANY;
5336       break;
5337
5338     case 0x14ce:        // growing steel wall (left/right)
5339       element = EL_EXPANDABLE_STEELWALL_HORIZONTAL;
5340       break;
5341
5342     case 0x14df:        // growing steel wall (up/down)
5343       element = EL_EXPANDABLE_STEELWALL_VERTICAL;
5344       break;
5345
5346     case 0x14e8:        // growing steel wall (up/down/left/right)
5347       element = EL_EXPANDABLE_STEELWALL_ANY;
5348       break;
5349
5350     case 0x14e9:
5351       element = EL_SHIELD_DEADLY;
5352       break;
5353
5354     case 0x1501:
5355       element = EL_EXTRA_TIME;
5356       break;
5357
5358     case 0x154f:
5359       element = EL_ACID;
5360       break;
5361
5362     case 0x1577:
5363       element = EL_EMPTY_SPACE;
5364       break;
5365
5366     case 0x1578:        // quicksand (empty)
5367       element = EL_QUICKSAND_FAST_EMPTY;
5368       break;
5369
5370     case 0x1579:        // slow quicksand (empty)
5371       element = EL_QUICKSAND_EMPTY;
5372       break;
5373
5374       // 0x157c - 0x158b:
5375       // EL_SAND
5376
5377       // 0x1590 - 0x159f:
5378       // EL_DC_LANDMINE
5379
5380     case 0x15a0:
5381       element = EL_EM_DYNAMITE;
5382       break;
5383
5384     case 0x15a1:        // key (red)
5385       element = EL_EM_KEY_1;
5386       break;
5387
5388     case 0x15a2:        // key (yellow)
5389       element = EL_EM_KEY_2;
5390       break;
5391
5392     case 0x15a3:        // key (blue)
5393       element = EL_EM_KEY_4;
5394       break;
5395
5396     case 0x15a4:        // key (green)
5397       element = EL_EM_KEY_3;
5398       break;
5399
5400     case 0x15a5:        // key (white)
5401       element = EL_DC_KEY_WHITE;
5402       break;
5403
5404     case 0x15a6:
5405       element = EL_WALL_SLIPPERY;
5406       break;
5407
5408     case 0x15a7:
5409       element = EL_WALL;
5410       break;
5411
5412     case 0x15a8:        // wall (not round)
5413       element = EL_WALL;
5414       break;
5415
5416     case 0x15a9:        // (blue)
5417       element = EL_CHAR_A;
5418       break;
5419
5420     case 0x15aa:        // (blue)
5421       element = EL_CHAR_B;
5422       break;
5423
5424     case 0x15ab:        // (blue)
5425       element = EL_CHAR_C;
5426       break;
5427
5428     case 0x15ac:        // (blue)
5429       element = EL_CHAR_D;
5430       break;
5431
5432     case 0x15ad:        // (blue)
5433       element = EL_CHAR_E;
5434       break;
5435
5436     case 0x15ae:        // (blue)
5437       element = EL_CHAR_F;
5438       break;
5439
5440     case 0x15af:        // (blue)
5441       element = EL_CHAR_G;
5442       break;
5443
5444     case 0x15b0:        // (blue)
5445       element = EL_CHAR_H;
5446       break;
5447
5448     case 0x15b1:        // (blue)
5449       element = EL_CHAR_I;
5450       break;
5451
5452     case 0x15b2:        // (blue)
5453       element = EL_CHAR_J;
5454       break;
5455
5456     case 0x15b3:        // (blue)
5457       element = EL_CHAR_K;
5458       break;
5459
5460     case 0x15b4:        // (blue)
5461       element = EL_CHAR_L;
5462       break;
5463
5464     case 0x15b5:        // (blue)
5465       element = EL_CHAR_M;
5466       break;
5467
5468     case 0x15b6:        // (blue)
5469       element = EL_CHAR_N;
5470       break;
5471
5472     case 0x15b7:        // (blue)
5473       element = EL_CHAR_O;
5474       break;
5475
5476     case 0x15b8:        // (blue)
5477       element = EL_CHAR_P;
5478       break;
5479
5480     case 0x15b9:        // (blue)
5481       element = EL_CHAR_Q;
5482       break;
5483
5484     case 0x15ba:        // (blue)
5485       element = EL_CHAR_R;
5486       break;
5487
5488     case 0x15bb:        // (blue)
5489       element = EL_CHAR_S;
5490       break;
5491
5492     case 0x15bc:        // (blue)
5493       element = EL_CHAR_T;
5494       break;
5495
5496     case 0x15bd:        // (blue)
5497       element = EL_CHAR_U;
5498       break;
5499
5500     case 0x15be:        // (blue)
5501       element = EL_CHAR_V;
5502       break;
5503
5504     case 0x15bf:        // (blue)
5505       element = EL_CHAR_W;
5506       break;
5507
5508     case 0x15c0:        // (blue)
5509       element = EL_CHAR_X;
5510       break;
5511
5512     case 0x15c1:        // (blue)
5513       element = EL_CHAR_Y;
5514       break;
5515
5516     case 0x15c2:        // (blue)
5517       element = EL_CHAR_Z;
5518       break;
5519
5520     case 0x15c3:        // (blue)
5521       element = EL_CHAR_AUMLAUT;
5522       break;
5523
5524     case 0x15c4:        // (blue)
5525       element = EL_CHAR_OUMLAUT;
5526       break;
5527
5528     case 0x15c5:        // (blue)
5529       element = EL_CHAR_UUMLAUT;
5530       break;
5531
5532     case 0x15c6:        // (blue)
5533       element = EL_CHAR_0;
5534       break;
5535
5536     case 0x15c7:        // (blue)
5537       element = EL_CHAR_1;
5538       break;
5539
5540     case 0x15c8:        // (blue)
5541       element = EL_CHAR_2;
5542       break;
5543
5544     case 0x15c9:        // (blue)
5545       element = EL_CHAR_3;
5546       break;
5547
5548     case 0x15ca:        // (blue)
5549       element = EL_CHAR_4;
5550       break;
5551
5552     case 0x15cb:        // (blue)
5553       element = EL_CHAR_5;
5554       break;
5555
5556     case 0x15cc:        // (blue)
5557       element = EL_CHAR_6;
5558       break;
5559
5560     case 0x15cd:        // (blue)
5561       element = EL_CHAR_7;
5562       break;
5563
5564     case 0x15ce:        // (blue)
5565       element = EL_CHAR_8;
5566       break;
5567
5568     case 0x15cf:        // (blue)
5569       element = EL_CHAR_9;
5570       break;
5571
5572     case 0x15d0:        // (blue)
5573       element = EL_CHAR_PERIOD;
5574       break;
5575
5576     case 0x15d1:        // (blue)
5577       element = EL_CHAR_EXCLAM;
5578       break;
5579
5580     case 0x15d2:        // (blue)
5581       element = EL_CHAR_COLON;
5582       break;
5583
5584     case 0x15d3:        // (blue)
5585       element = EL_CHAR_LESS;
5586       break;
5587
5588     case 0x15d4:        // (blue)
5589       element = EL_CHAR_GREATER;
5590       break;
5591
5592     case 0x15d5:        // (blue)
5593       element = EL_CHAR_QUESTION;
5594       break;
5595
5596     case 0x15d6:        // (blue)
5597       element = EL_CHAR_COPYRIGHT;
5598       break;
5599
5600     case 0x15d7:        // (blue)
5601       element = EL_CHAR_UP;
5602       break;
5603
5604     case 0x15d8:        // (blue)
5605       element = EL_CHAR_DOWN;
5606       break;
5607
5608     case 0x15d9:        // (blue)
5609       element = EL_CHAR_BUTTON;
5610       break;
5611
5612     case 0x15da:        // (blue)
5613       element = EL_CHAR_PLUS;
5614       break;
5615
5616     case 0x15db:        // (blue)
5617       element = EL_CHAR_MINUS;
5618       break;
5619
5620     case 0x15dc:        // (blue)
5621       element = EL_CHAR_APOSTROPHE;
5622       break;
5623
5624     case 0x15dd:        // (blue)
5625       element = EL_CHAR_PARENLEFT;
5626       break;
5627
5628     case 0x15de:        // (blue)
5629       element = EL_CHAR_PARENRIGHT;
5630       break;
5631
5632     case 0x15df:        // (green)
5633       element = EL_CHAR_A;
5634       break;
5635
5636     case 0x15e0:        // (green)
5637       element = EL_CHAR_B;
5638       break;
5639
5640     case 0x15e1:        // (green)
5641       element = EL_CHAR_C;
5642       break;
5643
5644     case 0x15e2:        // (green)
5645       element = EL_CHAR_D;
5646       break;
5647
5648     case 0x15e3:        // (green)
5649       element = EL_CHAR_E;
5650       break;
5651
5652     case 0x15e4:        // (green)
5653       element = EL_CHAR_F;
5654       break;
5655
5656     case 0x15e5:        // (green)
5657       element = EL_CHAR_G;
5658       break;
5659
5660     case 0x15e6:        // (green)
5661       element = EL_CHAR_H;
5662       break;
5663
5664     case 0x15e7:        // (green)
5665       element = EL_CHAR_I;
5666       break;
5667
5668     case 0x15e8:        // (green)
5669       element = EL_CHAR_J;
5670       break;
5671
5672     case 0x15e9:        // (green)
5673       element = EL_CHAR_K;
5674       break;
5675
5676     case 0x15ea:        // (green)
5677       element = EL_CHAR_L;
5678       break;
5679
5680     case 0x15eb:        // (green)
5681       element = EL_CHAR_M;
5682       break;
5683
5684     case 0x15ec:        // (green)
5685       element = EL_CHAR_N;
5686       break;
5687
5688     case 0x15ed:        // (green)
5689       element = EL_CHAR_O;
5690       break;
5691
5692     case 0x15ee:        // (green)
5693       element = EL_CHAR_P;
5694       break;
5695
5696     case 0x15ef:        // (green)
5697       element = EL_CHAR_Q;
5698       break;
5699
5700     case 0x15f0:        // (green)
5701       element = EL_CHAR_R;
5702       break;
5703
5704     case 0x15f1:        // (green)
5705       element = EL_CHAR_S;
5706       break;
5707
5708     case 0x15f2:        // (green)
5709       element = EL_CHAR_T;
5710       break;
5711
5712     case 0x15f3:        // (green)
5713       element = EL_CHAR_U;
5714       break;
5715
5716     case 0x15f4:        // (green)
5717       element = EL_CHAR_V;
5718       break;
5719
5720     case 0x15f5:        // (green)
5721       element = EL_CHAR_W;
5722       break;
5723
5724     case 0x15f6:        // (green)
5725       element = EL_CHAR_X;
5726       break;
5727
5728     case 0x15f7:        // (green)
5729       element = EL_CHAR_Y;
5730       break;
5731
5732     case 0x15f8:        // (green)
5733       element = EL_CHAR_Z;
5734       break;
5735
5736     case 0x15f9:        // (green)
5737       element = EL_CHAR_AUMLAUT;
5738       break;
5739
5740     case 0x15fa:        // (green)
5741       element = EL_CHAR_OUMLAUT;
5742       break;
5743
5744     case 0x15fb:        // (green)
5745       element = EL_CHAR_UUMLAUT;
5746       break;
5747
5748     case 0x15fc:        // (green)
5749       element = EL_CHAR_0;
5750       break;
5751
5752     case 0x15fd:        // (green)
5753       element = EL_CHAR_1;
5754       break;
5755
5756     case 0x15fe:        // (green)
5757       element = EL_CHAR_2;
5758       break;
5759
5760     case 0x15ff:        // (green)
5761       element = EL_CHAR_3;
5762       break;
5763
5764     case 0x1600:        // (green)
5765       element = EL_CHAR_4;
5766       break;
5767
5768     case 0x1601:        // (green)
5769       element = EL_CHAR_5;
5770       break;
5771
5772     case 0x1602:        // (green)
5773       element = EL_CHAR_6;
5774       break;
5775
5776     case 0x1603:        // (green)
5777       element = EL_CHAR_7;
5778       break;
5779
5780     case 0x1604:        // (green)
5781       element = EL_CHAR_8;
5782       break;
5783
5784     case 0x1605:        // (green)
5785       element = EL_CHAR_9;
5786       break;
5787
5788     case 0x1606:        // (green)
5789       element = EL_CHAR_PERIOD;
5790       break;
5791
5792     case 0x1607:        // (green)
5793       element = EL_CHAR_EXCLAM;
5794       break;
5795
5796     case 0x1608:        // (green)
5797       element = EL_CHAR_COLON;
5798       break;
5799
5800     case 0x1609:        // (green)
5801       element = EL_CHAR_LESS;
5802       break;
5803
5804     case 0x160a:        // (green)
5805       element = EL_CHAR_GREATER;
5806       break;
5807
5808     case 0x160b:        // (green)
5809       element = EL_CHAR_QUESTION;
5810       break;
5811
5812     case 0x160c:        // (green)
5813       element = EL_CHAR_COPYRIGHT;
5814       break;
5815
5816     case 0x160d:        // (green)
5817       element = EL_CHAR_UP;
5818       break;
5819
5820     case 0x160e:        // (green)
5821       element = EL_CHAR_DOWN;
5822       break;
5823
5824     case 0x160f:        // (green)
5825       element = EL_CHAR_BUTTON;
5826       break;
5827
5828     case 0x1610:        // (green)
5829       element = EL_CHAR_PLUS;
5830       break;
5831
5832     case 0x1611:        // (green)
5833       element = EL_CHAR_MINUS;
5834       break;
5835
5836     case 0x1612:        // (green)
5837       element = EL_CHAR_APOSTROPHE;
5838       break;
5839
5840     case 0x1613:        // (green)
5841       element = EL_CHAR_PARENLEFT;
5842       break;
5843
5844     case 0x1614:        // (green)
5845       element = EL_CHAR_PARENRIGHT;
5846       break;
5847
5848     case 0x1615:        // (blue steel)
5849       element = EL_STEEL_CHAR_A;
5850       break;
5851
5852     case 0x1616:        // (blue steel)
5853       element = EL_STEEL_CHAR_B;
5854       break;
5855
5856     case 0x1617:        // (blue steel)
5857       element = EL_STEEL_CHAR_C;
5858       break;
5859
5860     case 0x1618:        // (blue steel)
5861       element = EL_STEEL_CHAR_D;
5862       break;
5863
5864     case 0x1619:        // (blue steel)
5865       element = EL_STEEL_CHAR_E;
5866       break;
5867
5868     case 0x161a:        // (blue steel)
5869       element = EL_STEEL_CHAR_F;
5870       break;
5871
5872     case 0x161b:        // (blue steel)
5873       element = EL_STEEL_CHAR_G;
5874       break;
5875
5876     case 0x161c:        // (blue steel)
5877       element = EL_STEEL_CHAR_H;
5878       break;
5879
5880     case 0x161d:        // (blue steel)
5881       element = EL_STEEL_CHAR_I;
5882       break;
5883
5884     case 0x161e:        // (blue steel)
5885       element = EL_STEEL_CHAR_J;
5886       break;
5887
5888     case 0x161f:        // (blue steel)
5889       element = EL_STEEL_CHAR_K;
5890       break;
5891
5892     case 0x1620:        // (blue steel)
5893       element = EL_STEEL_CHAR_L;
5894       break;
5895
5896     case 0x1621:        // (blue steel)
5897       element = EL_STEEL_CHAR_M;
5898       break;
5899
5900     case 0x1622:        // (blue steel)
5901       element = EL_STEEL_CHAR_N;
5902       break;
5903
5904     case 0x1623:        // (blue steel)
5905       element = EL_STEEL_CHAR_O;
5906       break;
5907
5908     case 0x1624:        // (blue steel)
5909       element = EL_STEEL_CHAR_P;
5910       break;
5911
5912     case 0x1625:        // (blue steel)
5913       element = EL_STEEL_CHAR_Q;
5914       break;
5915
5916     case 0x1626:        // (blue steel)
5917       element = EL_STEEL_CHAR_R;
5918       break;
5919
5920     case 0x1627:        // (blue steel)
5921       element = EL_STEEL_CHAR_S;
5922       break;
5923
5924     case 0x1628:        // (blue steel)
5925       element = EL_STEEL_CHAR_T;
5926       break;
5927
5928     case 0x1629:        // (blue steel)
5929       element = EL_STEEL_CHAR_U;
5930       break;
5931
5932     case 0x162a:        // (blue steel)
5933       element = EL_STEEL_CHAR_V;
5934       break;
5935
5936     case 0x162b:        // (blue steel)
5937       element = EL_STEEL_CHAR_W;
5938       break;
5939
5940     case 0x162c:        // (blue steel)
5941       element = EL_STEEL_CHAR_X;
5942       break;
5943
5944     case 0x162d:        // (blue steel)
5945       element = EL_STEEL_CHAR_Y;
5946       break;
5947
5948     case 0x162e:        // (blue steel)
5949       element = EL_STEEL_CHAR_Z;
5950       break;
5951
5952     case 0x162f:        // (blue steel)
5953       element = EL_STEEL_CHAR_AUMLAUT;
5954       break;
5955
5956     case 0x1630:        // (blue steel)
5957       element = EL_STEEL_CHAR_OUMLAUT;
5958       break;
5959
5960     case 0x1631:        // (blue steel)
5961       element = EL_STEEL_CHAR_UUMLAUT;
5962       break;
5963
5964     case 0x1632:        // (blue steel)
5965       element = EL_STEEL_CHAR_0;
5966       break;
5967
5968     case 0x1633:        // (blue steel)
5969       element = EL_STEEL_CHAR_1;
5970       break;
5971
5972     case 0x1634:        // (blue steel)
5973       element = EL_STEEL_CHAR_2;
5974       break;
5975
5976     case 0x1635:        // (blue steel)
5977       element = EL_STEEL_CHAR_3;
5978       break;
5979
5980     case 0x1636:        // (blue steel)
5981       element = EL_STEEL_CHAR_4;
5982       break;
5983
5984     case 0x1637:        // (blue steel)
5985       element = EL_STEEL_CHAR_5;
5986       break;
5987
5988     case 0x1638:        // (blue steel)
5989       element = EL_STEEL_CHAR_6;
5990       break;
5991
5992     case 0x1639:        // (blue steel)
5993       element = EL_STEEL_CHAR_7;
5994       break;
5995
5996     case 0x163a:        // (blue steel)
5997       element = EL_STEEL_CHAR_8;
5998       break;
5999
6000     case 0x163b:        // (blue steel)
6001       element = EL_STEEL_CHAR_9;
6002       break;
6003
6004     case 0x163c:        // (blue steel)
6005       element = EL_STEEL_CHAR_PERIOD;
6006       break;
6007
6008     case 0x163d:        // (blue steel)
6009       element = EL_STEEL_CHAR_EXCLAM;
6010       break;
6011
6012     case 0x163e:        // (blue steel)
6013       element = EL_STEEL_CHAR_COLON;
6014       break;
6015
6016     case 0x163f:        // (blue steel)
6017       element = EL_STEEL_CHAR_LESS;
6018       break;
6019
6020     case 0x1640:        // (blue steel)
6021       element = EL_STEEL_CHAR_GREATER;
6022       break;
6023
6024     case 0x1641:        // (blue steel)
6025       element = EL_STEEL_CHAR_QUESTION;
6026       break;
6027
6028     case 0x1642:        // (blue steel)
6029       element = EL_STEEL_CHAR_COPYRIGHT;
6030       break;
6031
6032     case 0x1643:        // (blue steel)
6033       element = EL_STEEL_CHAR_UP;
6034       break;
6035
6036     case 0x1644:        // (blue steel)
6037       element = EL_STEEL_CHAR_DOWN;
6038       break;
6039
6040     case 0x1645:        // (blue steel)
6041       element = EL_STEEL_CHAR_BUTTON;
6042       break;
6043
6044     case 0x1646:        // (blue steel)
6045       element = EL_STEEL_CHAR_PLUS;
6046       break;
6047
6048     case 0x1647:        // (blue steel)
6049       element = EL_STEEL_CHAR_MINUS;
6050       break;
6051
6052     case 0x1648:        // (blue steel)
6053       element = EL_STEEL_CHAR_APOSTROPHE;
6054       break;
6055
6056     case 0x1649:        // (blue steel)
6057       element = EL_STEEL_CHAR_PARENLEFT;
6058       break;
6059
6060     case 0x164a:        // (blue steel)
6061       element = EL_STEEL_CHAR_PARENRIGHT;
6062       break;
6063
6064     case 0x164b:        // (green steel)
6065       element = EL_STEEL_CHAR_A;
6066       break;
6067
6068     case 0x164c:        // (green steel)
6069       element = EL_STEEL_CHAR_B;
6070       break;
6071
6072     case 0x164d:        // (green steel)
6073       element = EL_STEEL_CHAR_C;
6074       break;
6075
6076     case 0x164e:        // (green steel)
6077       element = EL_STEEL_CHAR_D;
6078       break;
6079
6080     case 0x164f:        // (green steel)
6081       element = EL_STEEL_CHAR_E;
6082       break;
6083
6084     case 0x1650:        // (green steel)
6085       element = EL_STEEL_CHAR_F;
6086       break;
6087
6088     case 0x1651:        // (green steel)
6089       element = EL_STEEL_CHAR_G;
6090       break;
6091
6092     case 0x1652:        // (green steel)
6093       element = EL_STEEL_CHAR_H;
6094       break;
6095
6096     case 0x1653:        // (green steel)
6097       element = EL_STEEL_CHAR_I;
6098       break;
6099
6100     case 0x1654:        // (green steel)
6101       element = EL_STEEL_CHAR_J;
6102       break;
6103
6104     case 0x1655:        // (green steel)
6105       element = EL_STEEL_CHAR_K;
6106       break;
6107
6108     case 0x1656:        // (green steel)
6109       element = EL_STEEL_CHAR_L;
6110       break;
6111
6112     case 0x1657:        // (green steel)
6113       element = EL_STEEL_CHAR_M;
6114       break;
6115
6116     case 0x1658:        // (green steel)
6117       element = EL_STEEL_CHAR_N;
6118       break;
6119
6120     case 0x1659:        // (green steel)
6121       element = EL_STEEL_CHAR_O;
6122       break;
6123
6124     case 0x165a:        // (green steel)
6125       element = EL_STEEL_CHAR_P;
6126       break;
6127
6128     case 0x165b:        // (green steel)
6129       element = EL_STEEL_CHAR_Q;
6130       break;
6131
6132     case 0x165c:        // (green steel)
6133       element = EL_STEEL_CHAR_R;
6134       break;
6135
6136     case 0x165d:        // (green steel)
6137       element = EL_STEEL_CHAR_S;
6138       break;
6139
6140     case 0x165e:        // (green steel)
6141       element = EL_STEEL_CHAR_T;
6142       break;
6143
6144     case 0x165f:        // (green steel)
6145       element = EL_STEEL_CHAR_U;
6146       break;
6147
6148     case 0x1660:        // (green steel)
6149       element = EL_STEEL_CHAR_V;
6150       break;
6151
6152     case 0x1661:        // (green steel)
6153       element = EL_STEEL_CHAR_W;
6154       break;
6155
6156     case 0x1662:        // (green steel)
6157       element = EL_STEEL_CHAR_X;
6158       break;
6159
6160     case 0x1663:        // (green steel)
6161       element = EL_STEEL_CHAR_Y;
6162       break;
6163
6164     case 0x1664:        // (green steel)
6165       element = EL_STEEL_CHAR_Z;
6166       break;
6167
6168     case 0x1665:        // (green steel)
6169       element = EL_STEEL_CHAR_AUMLAUT;
6170       break;
6171
6172     case 0x1666:        // (green steel)
6173       element = EL_STEEL_CHAR_OUMLAUT;
6174       break;
6175
6176     case 0x1667:        // (green steel)
6177       element = EL_STEEL_CHAR_UUMLAUT;
6178       break;
6179
6180     case 0x1668:        // (green steel)
6181       element = EL_STEEL_CHAR_0;
6182       break;
6183
6184     case 0x1669:        // (green steel)
6185       element = EL_STEEL_CHAR_1;
6186       break;
6187
6188     case 0x166a:        // (green steel)
6189       element = EL_STEEL_CHAR_2;
6190       break;
6191
6192     case 0x166b:        // (green steel)
6193       element = EL_STEEL_CHAR_3;
6194       break;
6195
6196     case 0x166c:        // (green steel)
6197       element = EL_STEEL_CHAR_4;
6198       break;
6199
6200     case 0x166d:        // (green steel)
6201       element = EL_STEEL_CHAR_5;
6202       break;
6203
6204     case 0x166e:        // (green steel)
6205       element = EL_STEEL_CHAR_6;
6206       break;
6207
6208     case 0x166f:        // (green steel)
6209       element = EL_STEEL_CHAR_7;
6210       break;
6211
6212     case 0x1670:        // (green steel)
6213       element = EL_STEEL_CHAR_8;
6214       break;
6215
6216     case 0x1671:        // (green steel)
6217       element = EL_STEEL_CHAR_9;
6218       break;
6219
6220     case 0x1672:        // (green steel)
6221       element = EL_STEEL_CHAR_PERIOD;
6222       break;
6223
6224     case 0x1673:        // (green steel)
6225       element = EL_STEEL_CHAR_EXCLAM;
6226       break;
6227
6228     case 0x1674:        // (green steel)
6229       element = EL_STEEL_CHAR_COLON;
6230       break;
6231
6232     case 0x1675:        // (green steel)
6233       element = EL_STEEL_CHAR_LESS;
6234       break;
6235
6236     case 0x1676:        // (green steel)
6237       element = EL_STEEL_CHAR_GREATER;
6238       break;
6239
6240     case 0x1677:        // (green steel)
6241       element = EL_STEEL_CHAR_QUESTION;
6242       break;
6243
6244     case 0x1678:        // (green steel)
6245       element = EL_STEEL_CHAR_COPYRIGHT;
6246       break;
6247
6248     case 0x1679:        // (green steel)
6249       element = EL_STEEL_CHAR_UP;
6250       break;
6251
6252     case 0x167a:        // (green steel)
6253       element = EL_STEEL_CHAR_DOWN;
6254       break;
6255
6256     case 0x167b:        // (green steel)
6257       element = EL_STEEL_CHAR_BUTTON;
6258       break;
6259
6260     case 0x167c:        // (green steel)
6261       element = EL_STEEL_CHAR_PLUS;
6262       break;
6263
6264     case 0x167d:        // (green steel)
6265       element = EL_STEEL_CHAR_MINUS;
6266       break;
6267
6268     case 0x167e:        // (green steel)
6269       element = EL_STEEL_CHAR_APOSTROPHE;
6270       break;
6271
6272     case 0x167f:        // (green steel)
6273       element = EL_STEEL_CHAR_PARENLEFT;
6274       break;
6275
6276     case 0x1680:        // (green steel)
6277       element = EL_STEEL_CHAR_PARENRIGHT;
6278       break;
6279
6280     case 0x1681:        // gate (red)
6281       element = EL_EM_GATE_1;
6282       break;
6283
6284     case 0x1682:        // secret gate (red)
6285       element = EL_EM_GATE_1_GRAY;
6286       break;
6287
6288     case 0x1683:        // gate (yellow)
6289       element = EL_EM_GATE_2;
6290       break;
6291
6292     case 0x1684:        // secret gate (yellow)
6293       element = EL_EM_GATE_2_GRAY;
6294       break;
6295
6296     case 0x1685:        // gate (blue)
6297       element = EL_EM_GATE_4;
6298       break;
6299
6300     case 0x1686:        // secret gate (blue)
6301       element = EL_EM_GATE_4_GRAY;
6302       break;
6303
6304     case 0x1687:        // gate (green)
6305       element = EL_EM_GATE_3;
6306       break;
6307
6308     case 0x1688:        // secret gate (green)
6309       element = EL_EM_GATE_3_GRAY;
6310       break;
6311
6312     case 0x1689:        // gate (white)
6313       element = EL_DC_GATE_WHITE;
6314       break;
6315
6316     case 0x168a:        // secret gate (white)
6317       element = EL_DC_GATE_WHITE_GRAY;
6318       break;
6319
6320     case 0x168b:        // secret gate (no key)
6321       element = EL_DC_GATE_FAKE_GRAY;
6322       break;
6323
6324     case 0x168c:
6325       element = EL_ROBOT_WHEEL;
6326       break;
6327
6328     case 0x168d:
6329       element = EL_DC_TIMEGATE_SWITCH;
6330       break;
6331
6332     case 0x168e:
6333       element = EL_ACID_POOL_BOTTOM;
6334       break;
6335
6336     case 0x168f:
6337       element = EL_ACID_POOL_TOPLEFT;
6338       break;
6339
6340     case 0x1690:
6341       element = EL_ACID_POOL_TOPRIGHT;
6342       break;
6343
6344     case 0x1691:
6345       element = EL_ACID_POOL_BOTTOMLEFT;
6346       break;
6347
6348     case 0x1692:
6349       element = EL_ACID_POOL_BOTTOMRIGHT;
6350       break;
6351
6352     case 0x1693:
6353       element = EL_STEELWALL;
6354       break;
6355
6356     case 0x1694:
6357       element = EL_STEELWALL_SLIPPERY;
6358       break;
6359
6360     case 0x1695:        // steel wall (not round)
6361       element = EL_STEELWALL;
6362       break;
6363
6364     case 0x1696:        // steel wall (left)
6365       element = EL_DC_STEELWALL_1_LEFT;
6366       break;
6367
6368     case 0x1697:        // steel wall (bottom)
6369       element = EL_DC_STEELWALL_1_BOTTOM;
6370       break;
6371
6372     case 0x1698:        // steel wall (right)
6373       element = EL_DC_STEELWALL_1_RIGHT;
6374       break;
6375
6376     case 0x1699:        // steel wall (top)
6377       element = EL_DC_STEELWALL_1_TOP;
6378       break;
6379
6380     case 0x169a:        // steel wall (left/bottom)
6381       element = EL_DC_STEELWALL_1_BOTTOMLEFT;
6382       break;
6383
6384     case 0x169b:        // steel wall (right/bottom)
6385       element = EL_DC_STEELWALL_1_BOTTOMRIGHT;
6386       break;
6387
6388     case 0x169c:        // steel wall (right/top)
6389       element = EL_DC_STEELWALL_1_TOPRIGHT;
6390       break;
6391
6392     case 0x169d:        // steel wall (left/top)
6393       element = EL_DC_STEELWALL_1_TOPLEFT;
6394       break;
6395
6396     case 0x169e:        // steel wall (right/bottom small)
6397       element = EL_DC_STEELWALL_1_BOTTOMRIGHT_2;
6398       break;
6399
6400     case 0x169f:        // steel wall (left/bottom small)
6401       element = EL_DC_STEELWALL_1_BOTTOMLEFT_2;
6402       break;
6403
6404     case 0x16a0:        // steel wall (right/top small)
6405       element = EL_DC_STEELWALL_1_TOPRIGHT_2;
6406       break;
6407
6408     case 0x16a1:        // steel wall (left/top small)
6409       element = EL_DC_STEELWALL_1_TOPLEFT_2;
6410       break;
6411
6412     case 0x16a2:        // steel wall (left/right)
6413       element = EL_DC_STEELWALL_1_VERTICAL;
6414       break;
6415
6416     case 0x16a3:        // steel wall (top/bottom)
6417       element = EL_DC_STEELWALL_1_HORIZONTAL;
6418       break;
6419
6420     case 0x16a4:        // steel wall 2 (left end)
6421       element = EL_DC_STEELWALL_2_LEFT;
6422       break;
6423
6424     case 0x16a5:        // steel wall 2 (right end)
6425       element = EL_DC_STEELWALL_2_RIGHT;
6426       break;
6427
6428     case 0x16a6:        // steel wall 2 (top end)
6429       element = EL_DC_STEELWALL_2_TOP;
6430       break;
6431
6432     case 0x16a7:        // steel wall 2 (bottom end)
6433       element = EL_DC_STEELWALL_2_BOTTOM;
6434       break;
6435
6436     case 0x16a8:        // steel wall 2 (left/right)
6437       element = EL_DC_STEELWALL_2_HORIZONTAL;
6438       break;
6439
6440     case 0x16a9:        // steel wall 2 (up/down)
6441       element = EL_DC_STEELWALL_2_VERTICAL;
6442       break;
6443
6444     case 0x16aa:        // steel wall 2 (mid)
6445       element = EL_DC_STEELWALL_2_MIDDLE;
6446       break;
6447
6448     case 0x16ab:
6449       element = EL_SIGN_EXCLAMATION;
6450       break;
6451
6452     case 0x16ac:
6453       element = EL_SIGN_RADIOACTIVITY;
6454       break;
6455
6456     case 0x16ad:
6457       element = EL_SIGN_STOP;
6458       break;
6459
6460     case 0x16ae:
6461       element = EL_SIGN_WHEELCHAIR;
6462       break;
6463
6464     case 0x16af:
6465       element = EL_SIGN_PARKING;
6466       break;
6467
6468     case 0x16b0:
6469       element = EL_SIGN_NO_ENTRY;
6470       break;
6471
6472     case 0x16b1:
6473       element = EL_SIGN_HEART;
6474       break;
6475
6476     case 0x16b2:
6477       element = EL_SIGN_GIVE_WAY;
6478       break;
6479
6480     case 0x16b3:
6481       element = EL_SIGN_ENTRY_FORBIDDEN;
6482       break;
6483
6484     case 0x16b4:
6485       element = EL_SIGN_EMERGENCY_EXIT;
6486       break;
6487
6488     case 0x16b5:
6489       element = EL_SIGN_YIN_YANG;
6490       break;
6491
6492     case 0x16b6:
6493       element = EL_WALL_EMERALD;
6494       break;
6495
6496     case 0x16b7:
6497       element = EL_WALL_DIAMOND;
6498       break;
6499
6500     case 0x16b8:
6501       element = EL_WALL_PEARL;
6502       break;
6503
6504     case 0x16b9:
6505       element = EL_WALL_CRYSTAL;
6506       break;
6507
6508     case 0x16ba:
6509       element = EL_INVISIBLE_WALL;
6510       break;
6511
6512     case 0x16bb:
6513       element = EL_INVISIBLE_STEELWALL;
6514       break;
6515
6516       // 0x16bc - 0x16cb:
6517       // EL_INVISIBLE_SAND
6518
6519     case 0x16cc:
6520       element = EL_LIGHT_SWITCH;
6521       break;
6522
6523     case 0x16cd:
6524       element = EL_ENVELOPE_1;
6525       break;
6526
6527     default:
6528       if (element >= 0x0117 && element <= 0x036e)       // (?)
6529         element = EL_DIAMOND;
6530       else if (element >= 0x042d && element <= 0x0684)  // (?)
6531         element = EL_EMERALD;
6532       else if (element >= 0x157c && element <= 0x158b)
6533         element = EL_SAND;
6534       else if (element >= 0x1590 && element <= 0x159f)
6535         element = EL_DC_LANDMINE;
6536       else if (element >= 0x16bc && element <= 0x16cb)
6537         element = EL_INVISIBLE_SAND;
6538       else
6539       {
6540         Warn("unknown Diamond Caves element 0x%04x", element);
6541
6542         element = EL_UNKNOWN;
6543       }
6544       break;
6545   }
6546
6547   return getMappedElement(element);
6548 }
6549
6550 static void LoadLevelFromFileStream_DC(File *file, struct LevelInfo *level)
6551 {
6552   byte header[DC_LEVEL_HEADER_SIZE];
6553   int envelope_size;
6554   int envelope_header_pos = 62;
6555   int envelope_content_pos = 94;
6556   int level_name_pos = 251;
6557   int level_author_pos = 292;
6558   int envelope_header_len;
6559   int envelope_content_len;
6560   int level_name_len;
6561   int level_author_len;
6562   int fieldx, fieldy;
6563   int num_yamyam_contents;
6564   int i, x, y;
6565
6566   getDecodedWord_DC(0, TRUE);           // initialize DC2 decoding engine
6567
6568   for (i = 0; i < DC_LEVEL_HEADER_SIZE / 2; i++)
6569   {
6570     unsigned short header_word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6571
6572     header[i * 2 + 0] = header_word >> 8;
6573     header[i * 2 + 1] = header_word & 0xff;
6574   }
6575
6576   // read some values from level header to check level decoding integrity
6577   fieldx = header[6] | (header[7] << 8);
6578   fieldy = header[8] | (header[9] << 8);
6579   num_yamyam_contents = header[60] | (header[61] << 8);
6580
6581   // do some simple sanity checks to ensure that level was correctly decoded
6582   if (fieldx < 1 || fieldx > 256 ||
6583       fieldy < 1 || fieldy > 256 ||
6584       num_yamyam_contents < 1 || num_yamyam_contents > 8)
6585   {
6586     level->no_valid_file = TRUE;
6587
6588     Warn("cannot decode level from stream -- using empty level");
6589
6590     return;
6591   }
6592
6593   // maximum envelope header size is 31 bytes
6594   envelope_header_len   = header[envelope_header_pos];
6595   // maximum envelope content size is 110 (156?) bytes
6596   envelope_content_len  = header[envelope_content_pos];
6597
6598   // maximum level title size is 40 bytes
6599   level_name_len        = MIN(header[level_name_pos],   MAX_LEVEL_NAME_LEN);
6600   // maximum level author size is 30 (51?) bytes
6601   level_author_len      = MIN(header[level_author_pos], MAX_LEVEL_AUTHOR_LEN);
6602
6603   envelope_size = 0;
6604
6605   for (i = 0; i < envelope_header_len; i++)
6606     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6607       level->envelope[0].text[envelope_size++] =
6608         header[envelope_header_pos + 1 + i];
6609
6610   if (envelope_header_len > 0 && envelope_content_len > 0)
6611   {
6612     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6613       level->envelope[0].text[envelope_size++] = '\n';
6614     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6615       level->envelope[0].text[envelope_size++] = '\n';
6616   }
6617
6618   for (i = 0; i < envelope_content_len; i++)
6619     if (envelope_size < MAX_ENVELOPE_TEXT_LEN)
6620       level->envelope[0].text[envelope_size++] =
6621         header[envelope_content_pos + 1 + i];
6622
6623   level->envelope[0].text[envelope_size] = '\0';
6624
6625   level->envelope[0].xsize = MAX_ENVELOPE_XSIZE;
6626   level->envelope[0].ysize = 10;
6627   level->envelope[0].autowrap = TRUE;
6628   level->envelope[0].centered = TRUE;
6629
6630   for (i = 0; i < level_name_len; i++)
6631     level->name[i] = header[level_name_pos + 1 + i];
6632   level->name[level_name_len] = '\0';
6633
6634   for (i = 0; i < level_author_len; i++)
6635     level->author[i] = header[level_author_pos + 1 + i];
6636   level->author[level_author_len] = '\0';
6637
6638   num_yamyam_contents = header[60] | (header[61] << 8);
6639   level->num_yamyam_contents =
6640     MIN(MAX(MIN_ELEMENT_CONTENTS, num_yamyam_contents), MAX_ELEMENT_CONTENTS);
6641
6642   for (i = 0; i < num_yamyam_contents; i++)
6643   {
6644     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
6645     {
6646       unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6647       int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6648
6649       if (i < MAX_ELEMENT_CONTENTS)
6650         level->yamyam_content[i].e[x][y] = getMappedElement_DC(element_dc);
6651     }
6652   }
6653
6654   fieldx = header[6] | (header[7] << 8);
6655   fieldy = header[8] | (header[9] << 8);
6656   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, fieldx), MAX_LEV_FIELDX);
6657   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, fieldy), MAX_LEV_FIELDY);
6658
6659   for (y = 0; y < fieldy; y++) for (x = 0; x < fieldx; x++)
6660   {
6661     unsigned short word = getDecodedWord_DC(getFile16BitBE(file), FALSE);
6662     int element_dc = ((word & 0xff) << 8) | ((word >> 8) & 0xff);
6663
6664     if (x < MAX_LEV_FIELDX && y < MAX_LEV_FIELDY)
6665       level->field[x][y] = getMappedElement_DC(element_dc);
6666   }
6667
6668   x = MIN(MAX(0, (header[10] | (header[11] << 8)) - 1), MAX_LEV_FIELDX - 1);
6669   y = MIN(MAX(0, (header[12] | (header[13] << 8)) - 1), MAX_LEV_FIELDY - 1);
6670   level->field[x][y] = EL_PLAYER_1;
6671
6672   x = MIN(MAX(0, (header[14] | (header[15] << 8)) - 1), MAX_LEV_FIELDX - 1);
6673   y = MIN(MAX(0, (header[16] | (header[17] << 8)) - 1), MAX_LEV_FIELDY - 1);
6674   level->field[x][y] = EL_PLAYER_2;
6675
6676   level->gems_needed            = header[18] | (header[19] << 8);
6677
6678   level->score[SC_EMERALD]      = header[20] | (header[21] << 8);
6679   level->score[SC_DIAMOND]      = header[22] | (header[23] << 8);
6680   level->score[SC_PEARL]        = header[24] | (header[25] << 8);
6681   level->score[SC_CRYSTAL]      = header[26] | (header[27] << 8);
6682   level->score[SC_NUT]          = header[28] | (header[29] << 8);
6683   level->score[SC_ROBOT]        = header[30] | (header[31] << 8);
6684   level->score[SC_SPACESHIP]    = header[32] | (header[33] << 8);
6685   level->score[SC_BUG]          = header[34] | (header[35] << 8);
6686   level->score[SC_YAMYAM]       = header[36] | (header[37] << 8);
6687   level->score[SC_DYNAMITE]     = header[38] | (header[39] << 8);
6688   level->score[SC_KEY]          = header[40] | (header[41] << 8);
6689   level->score[SC_TIME_BONUS]   = header[42] | (header[43] << 8);
6690
6691   level->time                   = header[44] | (header[45] << 8);
6692
6693   level->amoeba_speed           = header[46] | (header[47] << 8);
6694   level->time_light             = header[48] | (header[49] << 8);
6695   level->time_timegate          = header[50] | (header[51] << 8);
6696   level->time_wheel             = header[52] | (header[53] << 8);
6697   level->time_magic_wall        = header[54] | (header[55] << 8);
6698   level->extra_time             = header[56] | (header[57] << 8);
6699   level->shield_normal_time     = header[58] | (header[59] << 8);
6700
6701   // shield and extra time elements do not have a score
6702   level->score[SC_SHIELD]       = 0;
6703   level->extra_time_score       = 0;
6704
6705   // set time for normal and deadly shields to the same value
6706   level->shield_deadly_time     = level->shield_normal_time;
6707
6708   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
6709   // can slip down from flat walls, like normal walls and steel walls
6710   level->em_slippery_gems = TRUE;
6711
6712   // time score is counted for each 10 seconds left in Diamond Caves levels
6713   level->time_score_base = 10;
6714 }
6715
6716 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
6717                                      struct LevelFileInfo *level_file_info,
6718                                      boolean level_info_only)
6719 {
6720   char *filename = level_file_info->filename;
6721   File *file;
6722   int num_magic_bytes = 8;
6723   char magic_bytes[num_magic_bytes + 1];
6724   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6725
6726   if (!(file = openFile(filename, MODE_READ)))
6727   {
6728     level->no_valid_file = TRUE;
6729
6730     if (!level_info_only)
6731       Warn("cannot read level '%s' -- using empty level", filename);
6732
6733     return;
6734   }
6735
6736   // fseek(file, 0x0000, SEEK_SET);
6737
6738   if (level_file_info->packed)
6739   {
6740     // read "magic bytes" from start of file
6741     if (getStringFromFile(file, magic_bytes, num_magic_bytes + 1) == NULL)
6742       magic_bytes[0] = '\0';
6743
6744     // check "magic bytes" for correct file format
6745     if (!strPrefix(magic_bytes, "DC2"))
6746     {
6747       level->no_valid_file = TRUE;
6748
6749       Warn("unknown DC level file '%s' -- using empty level", filename);
6750
6751       return;
6752     }
6753
6754     if (strPrefix(magic_bytes, "DC2Win95") ||
6755         strPrefix(magic_bytes, "DC2Win98"))
6756     {
6757       int position_first_level = 0x00fa;
6758       int extra_bytes = 4;
6759       int skip_bytes;
6760
6761       // advance file stream to first level inside the level package
6762       skip_bytes = position_first_level - num_magic_bytes - extra_bytes;
6763
6764       // each block of level data is followed by block of non-level data
6765       num_levels_to_skip *= 2;
6766
6767       // at least skip header bytes, therefore use ">= 0" instead of "> 0"
6768       while (num_levels_to_skip >= 0)
6769       {
6770         // advance file stream to next level inside the level package
6771         if (seekFile(file, skip_bytes, SEEK_CUR) != 0)
6772         {
6773           level->no_valid_file = TRUE;
6774
6775           Warn("cannot fseek in file '%s' -- using empty level", filename);
6776
6777           return;
6778         }
6779
6780         // skip apparently unused extra bytes following each level
6781         ReadUnusedBytesFromFile(file, extra_bytes);
6782
6783         // read size of next level in level package
6784         skip_bytes = getFile32BitLE(file);
6785
6786         num_levels_to_skip--;
6787       }
6788     }
6789     else
6790     {
6791       level->no_valid_file = TRUE;
6792
6793       Warn("unknown DC2 level file '%s' -- using empty level", filename);
6794
6795       return;
6796     }
6797   }
6798
6799   LoadLevelFromFileStream_DC(file, level);
6800
6801   closeFile(file);
6802 }
6803
6804
6805 // ----------------------------------------------------------------------------
6806 // functions for loading SB level
6807 // ----------------------------------------------------------------------------
6808
6809 int getMappedElement_SB(int element_ascii, boolean use_ces)
6810 {
6811   static struct
6812   {
6813     int ascii;
6814     int sb;
6815     int ce;
6816   }
6817   sb_element_mapping[] =
6818   {
6819     { ' ', EL_EMPTY,                EL_CUSTOM_1 },  // floor (space)
6820     { '#', EL_STEELWALL,            EL_CUSTOM_2 },  // wall
6821     { '@', EL_PLAYER_1,             EL_CUSTOM_3 },  // player
6822     { '$', EL_SOKOBAN_OBJECT,       EL_CUSTOM_4 },  // box
6823     { '.', EL_SOKOBAN_FIELD_EMPTY,  EL_CUSTOM_5 },  // goal square
6824     { '*', EL_SOKOBAN_FIELD_FULL,   EL_CUSTOM_6 },  // box on goal square
6825     { '+', EL_SOKOBAN_FIELD_PLAYER, EL_CUSTOM_7 },  // player on goal square
6826     { '_', EL_INVISIBLE_STEELWALL,  EL_FROM_LEVEL_TEMPLATE },  // floor beyond border
6827
6828     { 0,   -1,                      -1          },
6829   };
6830
6831   int i;
6832
6833   for (i = 0; sb_element_mapping[i].ascii != 0; i++)
6834     if (element_ascii == sb_element_mapping[i].ascii)
6835       return (use_ces ? sb_element_mapping[i].ce : sb_element_mapping[i].sb);
6836
6837   return EL_UNDEFINED;
6838 }
6839
6840 static void SetLevelSettings_SB(struct LevelInfo *level)
6841 {
6842   // time settings
6843   level->time = 0;
6844   level->use_step_counter = TRUE;
6845
6846   // score settings
6847   level->score[SC_TIME_BONUS] = 0;
6848   level->time_score_base = 1;
6849   level->rate_time_over_score = TRUE;
6850
6851   // game settings
6852   level->auto_exit_sokoban = TRUE;
6853 }
6854
6855 static void LoadLevelFromFileInfo_SB(struct LevelInfo *level,
6856                                      struct LevelFileInfo *level_file_info,
6857                                      boolean level_info_only)
6858 {
6859   char *filename = level_file_info->filename;
6860   char line[MAX_LINE_LEN], line_raw[MAX_LINE_LEN], previous_line[MAX_LINE_LEN];
6861   char last_comment[MAX_LINE_LEN];
6862   char level_name[MAX_LINE_LEN];
6863   char *line_ptr;
6864   File *file;
6865   int num_levels_to_skip = level_file_info->nr - leveldir_current->first_level;
6866   boolean read_continued_line = FALSE;
6867   boolean reading_playfield = FALSE;
6868   boolean got_valid_playfield_line = FALSE;
6869   boolean invalid_playfield_char = FALSE;
6870   boolean load_xsb_to_ces = check_special_flags("load_xsb_to_ces");
6871   int file_level_nr = 0;
6872   int x = 0, y = 0;             // initialized to make compilers happy
6873
6874   last_comment[0] = '\0';
6875   level_name[0] = '\0';
6876
6877   if (!(file = openFile(filename, MODE_READ)))
6878   {
6879     level->no_valid_file = TRUE;
6880
6881     if (!level_info_only)
6882       Warn("cannot read level '%s' -- using empty level", filename);
6883
6884     return;
6885   }
6886
6887   while (!checkEndOfFile(file))
6888   {
6889     // level successfully read, but next level may follow here
6890     if (!got_valid_playfield_line && reading_playfield)
6891     {
6892       // read playfield from single level file -- skip remaining file
6893       if (!level_file_info->packed)
6894         break;
6895
6896       if (file_level_nr >= num_levels_to_skip)
6897         break;
6898
6899       file_level_nr++;
6900
6901       last_comment[0] = '\0';
6902       level_name[0] = '\0';
6903
6904       reading_playfield = FALSE;
6905     }
6906
6907     got_valid_playfield_line = FALSE;
6908
6909     // read next line of input file
6910     if (!getStringFromFile(file, line, MAX_LINE_LEN))
6911       break;
6912
6913     // cut trailing line break (this can be newline and/or carriage return)
6914     for (line_ptr = &line[strlen(line)]; line_ptr >= line; line_ptr--)
6915       if ((*line_ptr == '\n' || *line_ptr == '\r') && *(line_ptr + 1) == '\0')
6916         *line_ptr = '\0';
6917
6918     // copy raw input line for later use (mainly debugging output)
6919     strcpy(line_raw, line);
6920
6921     if (read_continued_line)
6922     {
6923       // append new line to existing line, if there is enough space
6924       if (strlen(previous_line) + strlen(line_ptr) < MAX_LINE_LEN)
6925         strcat(previous_line, line_ptr);
6926
6927       strcpy(line, previous_line);      // copy storage buffer to line
6928
6929       read_continued_line = FALSE;
6930     }
6931
6932     // if the last character is '\', continue at next line
6933     if (strlen(line) > 0 && line[strlen(line) - 1] == '\\')
6934     {
6935       line[strlen(line) - 1] = '\0';    // cut off trailing backslash
6936       strcpy(previous_line, line);      // copy line to storage buffer
6937
6938       read_continued_line = TRUE;
6939
6940       continue;
6941     }
6942
6943     // skip empty lines
6944     if (line[0] == '\0')
6945       continue;
6946
6947     // extract comment text from comment line
6948     if (line[0] == ';')
6949     {
6950       for (line_ptr = line; *line_ptr; line_ptr++)
6951         if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != ';')
6952           break;
6953
6954       strcpy(last_comment, line_ptr);
6955
6956       continue;
6957     }
6958
6959     // extract level title text from line containing level title
6960     if (line[0] == '\'')
6961     {
6962       strcpy(level_name, &line[1]);
6963
6964       if (strlen(level_name) > 0 && level_name[strlen(level_name) - 1] == '\'')
6965         level_name[strlen(level_name) - 1] = '\0';
6966
6967       continue;
6968     }
6969
6970     // skip lines containing only spaces (or empty lines)
6971     for (line_ptr = line; *line_ptr; line_ptr++)
6972       if (*line_ptr != ' ')
6973         break;
6974     if (*line_ptr == '\0')
6975       continue;
6976
6977     // at this point, we have found a line containing part of a playfield
6978
6979     got_valid_playfield_line = TRUE;
6980
6981     if (!reading_playfield)
6982     {
6983       reading_playfield = TRUE;
6984       invalid_playfield_char = FALSE;
6985
6986       for (x = 0; x < MAX_LEV_FIELDX; x++)
6987         for (y = 0; y < MAX_LEV_FIELDY; y++)
6988           level->field[x][y] = getMappedElement_SB(' ', load_xsb_to_ces);
6989
6990       level->fieldx = 0;
6991       level->fieldy = 0;
6992
6993       // start with topmost tile row
6994       y = 0;
6995     }
6996
6997     // skip playfield line if larger row than allowed
6998     if (y >= MAX_LEV_FIELDY)
6999       continue;
7000
7001     // start with leftmost tile column
7002     x = 0;
7003
7004     // read playfield elements from line
7005     for (line_ptr = line; *line_ptr; line_ptr++)
7006     {
7007       int mapped_sb_element = getMappedElement_SB(*line_ptr, load_xsb_to_ces);
7008
7009       // stop parsing playfield line if larger column than allowed
7010       if (x >= MAX_LEV_FIELDX)
7011         break;
7012
7013       if (mapped_sb_element == EL_UNDEFINED)
7014       {
7015         invalid_playfield_char = TRUE;
7016
7017         break;
7018       }
7019
7020       level->field[x][y] = mapped_sb_element;
7021
7022       // continue with next tile column
7023       x++;
7024
7025       level->fieldx = MAX(x, level->fieldx);
7026     }
7027
7028     if (invalid_playfield_char)
7029     {
7030       // if first playfield line, treat invalid lines as comment lines
7031       if (y == 0)
7032         reading_playfield = FALSE;
7033
7034       continue;
7035     }
7036
7037     // continue with next tile row
7038     y++;
7039   }
7040
7041   closeFile(file);
7042
7043   level->fieldy = y;
7044
7045   level->fieldx = MIN(MAX(MIN_LEV_FIELDX, level->fieldx), MAX_LEV_FIELDX);
7046   level->fieldy = MIN(MAX(MIN_LEV_FIELDY, level->fieldy), MAX_LEV_FIELDY);
7047
7048   if (!reading_playfield)
7049   {
7050     level->no_valid_file = TRUE;
7051
7052     Warn("cannot read level '%s' -- using empty level", filename);
7053
7054     return;
7055   }
7056
7057   if (*level_name != '\0')
7058   {
7059     strncpy(level->name, level_name, MAX_LEVEL_NAME_LEN);
7060     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7061   }
7062   else if (*last_comment != '\0')
7063   {
7064     strncpy(level->name, last_comment, MAX_LEVEL_NAME_LEN);
7065     level->name[MAX_LEVEL_NAME_LEN] = '\0';
7066   }
7067   else
7068   {
7069     sprintf(level->name, "--> Level %d <--", level_file_info->nr);
7070   }
7071
7072   // set all empty fields beyond the border walls to invisible steel wall
7073   for (y = 0; y < level->fieldy; y++) for (x = 0; x < level->fieldx; x++)
7074   {
7075     if ((x == 0 || x == level->fieldx - 1 ||
7076          y == 0 || y == level->fieldy - 1) &&
7077         level->field[x][y] == getMappedElement_SB(' ', load_xsb_to_ces))
7078       FloodFillLevel(x, y, getMappedElement_SB('_', load_xsb_to_ces),
7079                      level->field, level->fieldx, level->fieldy);
7080   }
7081
7082   // set special level settings for Sokoban levels
7083   SetLevelSettings_SB(level);
7084
7085   if (load_xsb_to_ces)
7086   {
7087     // special global settings can now be set in level template
7088     level->use_custom_template = TRUE;
7089   }
7090 }
7091
7092
7093 // -------------------------------------------------------------------------
7094 // functions for handling native levels
7095 // -------------------------------------------------------------------------
7096
7097 static void LoadLevelFromFileInfo_BD(struct LevelInfo *level,
7098                                      struct LevelFileInfo *level_file_info,
7099                                      boolean level_info_only)
7100 {
7101   int pos = 0;
7102
7103   // determine position of requested level inside level package
7104   if (level_file_info->packed)
7105     pos = level_file_info->nr - leveldir_current->first_level;
7106
7107   if (!LoadNativeLevel_BD(level_file_info->filename, pos, level_info_only))
7108     level->no_valid_file = TRUE;
7109 }
7110
7111 static void LoadLevelFromFileInfo_EM(struct LevelInfo *level,
7112                                      struct LevelFileInfo *level_file_info,
7113                                      boolean level_info_only)
7114 {
7115   if (!LoadNativeLevel_EM(level_file_info->filename, level_info_only))
7116     level->no_valid_file = TRUE;
7117 }
7118
7119 static void LoadLevelFromFileInfo_SP(struct LevelInfo *level,
7120                                      struct LevelFileInfo *level_file_info,
7121                                      boolean level_info_only)
7122 {
7123   int pos = 0;
7124
7125   // determine position of requested level inside level package
7126   if (level_file_info->packed)
7127     pos = level_file_info->nr - leveldir_current->first_level;
7128
7129   if (!LoadNativeLevel_SP(level_file_info->filename, pos, level_info_only))
7130     level->no_valid_file = TRUE;
7131 }
7132
7133 static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
7134                                      struct LevelFileInfo *level_file_info,
7135                                      boolean level_info_only)
7136 {
7137   if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
7138     level->no_valid_file = TRUE;
7139 }
7140
7141 void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
7142 {
7143   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7144     CopyNativeLevel_RND_to_BD(level);
7145   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7146     CopyNativeLevel_RND_to_EM(level);
7147   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7148     CopyNativeLevel_RND_to_SP(level);
7149   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7150     CopyNativeLevel_RND_to_MM(level);
7151 }
7152
7153 void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
7154 {
7155   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7156     CopyNativeLevel_BD_to_RND(level);
7157   else if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
7158     CopyNativeLevel_EM_to_RND(level);
7159   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7160     CopyNativeLevel_SP_to_RND(level);
7161   else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
7162     CopyNativeLevel_MM_to_RND(level);
7163 }
7164
7165 void SaveNativeLevel(struct LevelInfo *level)
7166 {
7167   // saving native level files only supported for some game engines
7168   if (level->game_engine_type != GAME_ENGINE_TYPE_BD &&
7169       level->game_engine_type != GAME_ENGINE_TYPE_SP)
7170     return;
7171
7172   char *file_ext = (level->game_engine_type == GAME_ENGINE_TYPE_BD ? "bd" :
7173                     level->game_engine_type == GAME_ENGINE_TYPE_SP ? "sp" : "");
7174   char *basename = getSingleLevelBasenameExt(level->file_info.nr, file_ext);
7175   char *filename = getLevelFilenameFromBasename(basename);
7176
7177   if (fileExists(filename) && !Request("Native level file already exists! Overwrite it?", REQ_ASK))
7178     return;
7179
7180   boolean success = FALSE;
7181
7182   if (level->game_engine_type == GAME_ENGINE_TYPE_BD)
7183   {
7184     CopyNativeLevel_RND_to_BD(level);
7185     // CopyNativeTape_RND_to_BD(level);
7186
7187     success = SaveNativeLevel_BD(filename);
7188   }
7189   else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
7190   {
7191     CopyNativeLevel_RND_to_SP(level);
7192     CopyNativeTape_RND_to_SP(level);
7193
7194     success = SaveNativeLevel_SP(filename);
7195   }
7196
7197   if (success)
7198     Request("Native level file saved!", REQ_CONFIRM);
7199   else
7200     Request("Failed to save native level file!", REQ_CONFIRM);
7201 }
7202
7203
7204 // ----------------------------------------------------------------------------
7205 // functions for loading generic level
7206 // ----------------------------------------------------------------------------
7207
7208 static void LoadLevelFromFileInfo(struct LevelInfo *level,
7209                                   struct LevelFileInfo *level_file_info,
7210                                   boolean level_info_only)
7211 {
7212   // always start with reliable default values
7213   setLevelInfoToDefaults(level, level_info_only, TRUE);
7214
7215   switch (level_file_info->type)
7216   {
7217     case LEVEL_FILE_TYPE_RND:
7218       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7219       break;
7220
7221     case LEVEL_FILE_TYPE_BD:
7222       LoadLevelFromFileInfo_BD(level, level_file_info, level_info_only);
7223       level->game_engine_type = GAME_ENGINE_TYPE_BD;
7224       break;
7225
7226     case LEVEL_FILE_TYPE_EM:
7227       LoadLevelFromFileInfo_EM(level, level_file_info, level_info_only);
7228       level->game_engine_type = GAME_ENGINE_TYPE_EM;
7229       break;
7230
7231     case LEVEL_FILE_TYPE_SP:
7232       LoadLevelFromFileInfo_SP(level, level_file_info, level_info_only);
7233       level->game_engine_type = GAME_ENGINE_TYPE_SP;
7234       break;
7235
7236     case LEVEL_FILE_TYPE_MM:
7237       LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
7238       level->game_engine_type = GAME_ENGINE_TYPE_MM;
7239       break;
7240
7241     case LEVEL_FILE_TYPE_DC:
7242       LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
7243       break;
7244
7245     case LEVEL_FILE_TYPE_SB:
7246       LoadLevelFromFileInfo_SB(level, level_file_info, level_info_only);
7247       break;
7248
7249     default:
7250       LoadLevelFromFileInfo_RND(level, level_file_info, level_info_only);
7251       break;
7252   }
7253
7254   // if level file is invalid, restore level structure to default values
7255   if (level->no_valid_file)
7256     setLevelInfoToDefaults(level, level_info_only, FALSE);
7257
7258   if (check_special_flags("use_native_bd_game_engine"))
7259     level->game_engine_type = GAME_ENGINE_TYPE_BD;
7260
7261   if (level->game_engine_type == GAME_ENGINE_TYPE_UNKNOWN)
7262     level->game_engine_type = GAME_ENGINE_TYPE_RND;
7263
7264   if (level_file_info->type != LEVEL_FILE_TYPE_RND)
7265     CopyNativeLevel_Native_to_RND(level);
7266 }
7267
7268 void LoadLevelFromFilename(struct LevelInfo *level, char *filename)
7269 {
7270   static struct LevelFileInfo level_file_info;
7271
7272   // always start with reliable default values
7273   setFileInfoToDefaults(&level_file_info);
7274
7275   level_file_info.nr = 0;                       // unknown level number
7276   level_file_info.type = LEVEL_FILE_TYPE_RND;   // no others supported yet
7277
7278   setString(&level_file_info.filename, filename);
7279
7280   LoadLevelFromFileInfo(level, &level_file_info, FALSE);
7281 }
7282
7283 static void LoadLevel_InitVersion(struct LevelInfo *level)
7284 {
7285   int i, j;
7286
7287   if (leveldir_current == NULL)         // only when dumping level
7288     return;
7289
7290   // all engine modifications also valid for levels which use latest engine
7291   if (level->game_version < VERSION_IDENT(3,2,0,5))
7292   {
7293     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
7294     level->time_score_base = 10;
7295   }
7296
7297   if (leveldir_current->latest_engine)
7298   {
7299     // ---------- use latest game engine --------------------------------------
7300
7301     /* For all levels which are forced to use the latest game engine version
7302        (normally all but user contributed, private and undefined levels), set
7303        the game engine version to the actual version; this allows for actual
7304        corrections in the game engine to take effect for existing, converted
7305        levels (from "classic" or other existing games) to make the emulation
7306        of the corresponding game more accurate, while (hopefully) not breaking
7307        existing levels created from other players. */
7308
7309     level->game_version = GAME_VERSION_ACTUAL;
7310
7311     /* Set special EM style gems behaviour: EM style gems slip down from
7312        normal, steel and growing wall. As this is a more fundamental change,
7313        it seems better to set the default behaviour to "off" (as it is more
7314        natural) and make it configurable in the level editor (as a property
7315        of gem style elements). Already existing converted levels (neither
7316        private nor contributed levels) are changed to the new behaviour. */
7317
7318     if (level->file_version < FILE_VERSION_2_0)
7319       level->em_slippery_gems = TRUE;
7320
7321     return;
7322   }
7323
7324   // ---------- use game engine the level was created with --------------------
7325
7326   /* For all levels which are not forced to use the latest game engine
7327      version (normally user contributed, private and undefined levels),
7328      use the version of the game engine the levels were created for.
7329
7330      Since 2.0.1, the game engine version is now directly stored
7331      in the level file (chunk "VERS"), so there is no need anymore
7332      to set the game version from the file version (except for old,
7333      pre-2.0 levels, where the game version is still taken from the
7334      file format version used to store the level -- see above). */
7335
7336   // player was faster than enemies in 1.0.0 and before
7337   if (level->file_version == FILE_VERSION_1_0)
7338     for (i = 0; i < MAX_PLAYERS; i++)
7339       level->initial_player_stepsize[i] = STEPSIZE_FAST;
7340
7341   // default behaviour for EM style gems was "slippery" only in 2.0.1
7342   if (level->game_version == VERSION_IDENT(2,0,1,0))
7343     level->em_slippery_gems = TRUE;
7344
7345   // springs could be pushed over pits before (pre-release version) 2.2.0
7346   if (level->game_version < VERSION_IDENT(2,2,0,0))
7347     level->use_spring_bug = TRUE;
7348
7349   if (level->game_version < VERSION_IDENT(3,2,0,5))
7350   {
7351     // time orb caused limited time in endless time levels before 3.2.0-5
7352     level->use_time_orb_bug = TRUE;
7353
7354     // default behaviour for snapping was "no snap delay" before 3.2.0-5
7355     level->block_snap_field = FALSE;
7356
7357     // extra time score was same value as time left score before 3.2.0-5
7358     level->extra_time_score = level->score[SC_TIME_BONUS];
7359   }
7360
7361   if (level->game_version < VERSION_IDENT(3,2,0,7))
7362   {
7363     // default behaviour for snapping was "not continuous" before 3.2.0-7
7364     level->continuous_snapping = FALSE;
7365   }
7366
7367   // only few elements were able to actively move into acid before 3.1.0
7368   // trigger settings did not exist before 3.1.0; set to default "any"
7369   if (level->game_version < VERSION_IDENT(3,1,0,0))
7370   {
7371     // correct "can move into acid" settings (all zero in old levels)
7372
7373     level->can_move_into_acid_bits = 0; // nothing can move into acid
7374     level->dont_collide_with_bits = 0; // nothing is deadly when colliding
7375
7376     setMoveIntoAcidProperty(level, EL_ROBOT,     TRUE);
7377     setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
7378     setMoveIntoAcidProperty(level, EL_PENGUIN,   TRUE);
7379     setMoveIntoAcidProperty(level, EL_BALLOON,   TRUE);
7380
7381     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7382       SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
7383
7384     // correct trigger settings (stored as zero == "none" in old levels)
7385
7386     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7387     {
7388       int element = EL_CUSTOM_START + i;
7389       struct ElementInfo *ei = &element_info[element];
7390
7391       for (j = 0; j < ei->num_change_pages; j++)
7392       {
7393         struct ElementChangeInfo *change = &ei->change_page[j];
7394
7395         change->trigger_player = CH_PLAYER_ANY;
7396         change->trigger_page = CH_PAGE_ANY;
7397       }
7398     }
7399   }
7400
7401   // try to detect and fix "Snake Bite" levels, which are broken with 3.2.0
7402   {
7403     int element = EL_CUSTOM_256;
7404     struct ElementInfo *ei = &element_info[element];
7405     struct ElementChangeInfo *change = &ei->change_page[0];
7406
7407     /* This is needed to fix a problem that was caused by a bugfix in function
7408        game.c/CreateFieldExt() introduced with 3.2.0 that corrects the behaviour
7409        when a custom element changes to EL_SOKOBAN_FIELD_PLAYER (before, it did
7410        not replace walkable elements, but instead just placed the player on it,
7411        without placing the Sokoban field under the player). Unfortunately, this
7412        breaks "Snake Bite" style levels when the snake is halfway through a door
7413        that just closes (the snake head is still alive and can be moved in this
7414        case). This can be fixed by replacing the EL_SOKOBAN_FIELD_PLAYER by the
7415        player (without Sokoban element) which then gets killed as designed). */
7416
7417     if ((strncmp(leveldir_current->identifier, "snake_bite", 10) == 0 ||
7418          strncmp(ei->description, "pause b4 death", 14) == 0) &&
7419         change->target_element == EL_SOKOBAN_FIELD_PLAYER)
7420       change->target_element = EL_PLAYER_1;
7421   }
7422
7423   // try to detect and fix "Zelda" style levels, which are broken with 3.2.5
7424   if (level->game_version < VERSION_IDENT(3,2,5,0))
7425   {
7426     /* This is needed to fix a problem that was caused by a bugfix in function
7427        game.c/CheckTriggeredElementChangeExt() introduced with 3.2.5 that
7428        corrects the behaviour when a custom element changes to another custom
7429        element with a higher element number that has change actions defined.
7430        Normally, only one change per frame is allowed for custom elements.
7431        Therefore, it is checked if a custom element already changed in the
7432        current frame; if it did, subsequent changes are suppressed.
7433        Unfortunately, this is only checked for element changes, but not for
7434        change actions, which are still executed. As the function above loops
7435        through all custom elements from lower to higher, an element change
7436        resulting in a lower CE number won't be checked again, while a target
7437        element with a higher number will also be checked, and potential change
7438        actions will get executed for this CE, too (which is wrong), while
7439        further changes are ignored (which is correct). As this bugfix breaks
7440        Zelda II (and introduces graphical bugs to Zelda I, and also breaks a
7441        few other levels like Alan Bond's "FMV"), allow the previous, incorrect
7442        behaviour for existing levels and tapes that make use of this bug */
7443
7444     level->use_action_after_change_bug = TRUE;
7445   }
7446
7447   // not centering level after relocating player was default only in 3.2.3
7448   if (level->game_version == VERSION_IDENT(3,2,3,0))    // (no pre-releases)
7449     level->shifted_relocation = TRUE;
7450
7451   // EM style elements always chain-exploded in R'n'D engine before 3.2.6
7452   if (level->game_version < VERSION_IDENT(3,2,6,0))
7453     level->em_explodes_by_fire = TRUE;
7454
7455   // levels were solved by the first player entering an exit up to 4.1.0.0
7456   if (level->game_version <= VERSION_IDENT(4,1,0,0))
7457     level->solved_by_one_player = TRUE;
7458
7459   // game logic of "game of life" and "biomaze" was buggy before 4.1.1.1
7460   if (level->game_version < VERSION_IDENT(4,1,1,1))
7461     level->use_life_bugs = TRUE;
7462
7463   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
7464   if (level->game_version < VERSION_IDENT(4,1,1,1))
7465     level->sb_objects_needed = FALSE;
7466
7467   // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
7468   if (level->game_version <= VERSION_IDENT(4,2,2,0))
7469     level->finish_dig_collect = FALSE;
7470
7471   // CE changing to player was kept under the player if walkable up to 4.2.3.1
7472   if (level->game_version <= VERSION_IDENT(4,2,3,1))
7473     level->keep_walkable_ce = TRUE;
7474 }
7475
7476 static void LoadLevel_InitSettings_SB(struct LevelInfo *level)
7477 {
7478   boolean is_sokoban_level = TRUE;    // unless non-Sokoban elements found
7479   int x, y;
7480
7481   // check if this level is (not) a Sokoban level
7482   for (y = 0; y < level->fieldy; y++)
7483     for (x = 0; x < level->fieldx; x++)
7484       if (!IS_SB_ELEMENT(Tile[x][y]))
7485         is_sokoban_level = FALSE;
7486
7487   if (is_sokoban_level)
7488   {
7489     // set special level settings for Sokoban levels
7490     SetLevelSettings_SB(level);
7491   }
7492 }
7493
7494 static void LoadLevel_InitSettings(struct LevelInfo *level)
7495 {
7496   // adjust level settings for (non-native) Sokoban-style levels
7497   LoadLevel_InitSettings_SB(level);
7498
7499   // rename levels with title "nameless level" or if renaming is forced
7500   if (leveldir_current->empty_level_name != NULL &&
7501       (strEqual(level->name, NAMELESS_LEVEL_NAME) ||
7502        leveldir_current->force_level_name))
7503     snprintf(level->name, MAX_LEVEL_NAME_LEN + 1,
7504              leveldir_current->empty_level_name, level_nr);
7505 }
7506
7507 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
7508 {
7509   int i, x, y;
7510
7511   // map elements that have changed in newer versions
7512   level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
7513                                                     level->game_version);
7514   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7515     for (x = 0; x < 3; x++)
7516       for (y = 0; y < 3; y++)
7517         level->yamyam_content[i].e[x][y] =
7518           getMappedElementByVersion(level->yamyam_content[i].e[x][y],
7519                                     level->game_version);
7520
7521 }
7522
7523 static void LoadLevel_InitCustomElements(struct LevelInfo *level)
7524 {
7525   int i, j;
7526
7527   // map custom element change events that have changed in newer versions
7528   // (these following values were accidentally changed in version 3.0.1)
7529   // (this seems to be needed only for 'ab_levelset3' and 'ab_levelset4')
7530   if (level->game_version <= VERSION_IDENT(3,0,0,0))
7531   {
7532     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7533     {
7534       int element = EL_CUSTOM_START + i;
7535
7536       // order of checking and copying events to be mapped is important
7537       // (do not change the start and end value -- they are constant)
7538       for (j = CE_BY_OTHER_ACTION; j >= CE_VALUE_GETS_ZERO; j--)
7539       {
7540         if (HAS_CHANGE_EVENT(element, j - 2))
7541         {
7542           SET_CHANGE_EVENT(element, j - 2, FALSE);
7543           SET_CHANGE_EVENT(element, j, TRUE);
7544         }
7545       }
7546
7547       // order of checking and copying events to be mapped is important
7548       // (do not change the start and end value -- they are constant)
7549       for (j = CE_PLAYER_COLLECTS_X; j >= CE_HITTING_SOMETHING; j--)
7550       {
7551         if (HAS_CHANGE_EVENT(element, j - 1))
7552         {
7553           SET_CHANGE_EVENT(element, j - 1, FALSE);
7554           SET_CHANGE_EVENT(element, j, TRUE);
7555         }
7556       }
7557     }
7558   }
7559
7560   // initialize "can_change" field for old levels with only one change page
7561   if (level->game_version <= VERSION_IDENT(3,0,2,0))
7562   {
7563     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7564     {
7565       int element = EL_CUSTOM_START + i;
7566
7567       if (CAN_CHANGE(element))
7568         element_info[element].change->can_change = TRUE;
7569     }
7570   }
7571
7572   // correct custom element values (for old levels without these options)
7573   if (level->game_version < VERSION_IDENT(3,1,1,0))
7574   {
7575     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7576     {
7577       int element = EL_CUSTOM_START + i;
7578       struct ElementInfo *ei = &element_info[element];
7579
7580       if (ei->access_direction == MV_NO_DIRECTION)
7581         ei->access_direction = MV_ALL_DIRECTIONS;
7582     }
7583   }
7584
7585   // correct custom element values (fix invalid values for all versions)
7586   if (1)
7587   {
7588     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7589     {
7590       int element = EL_CUSTOM_START + i;
7591       struct ElementInfo *ei = &element_info[element];
7592
7593       for (j = 0; j < ei->num_change_pages; j++)
7594       {
7595         struct ElementChangeInfo *change = &ei->change_page[j];
7596
7597         if (change->trigger_player == CH_PLAYER_NONE)
7598           change->trigger_player = CH_PLAYER_ANY;
7599
7600         if (change->trigger_side == CH_SIDE_NONE)
7601           change->trigger_side = CH_SIDE_ANY;
7602       }
7603     }
7604   }
7605
7606   // initialize "can_explode" field for old levels which did not store this
7607   // !!! CHECK THIS -- "<= 3,1,0,0" IS PROBABLY WRONG !!!
7608   if (level->game_version <= VERSION_IDENT(3,1,0,0))
7609   {
7610     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7611     {
7612       int element = EL_CUSTOM_START + i;
7613
7614       if (EXPLODES_1X1_OLD(element))
7615         element_info[element].explosion_type = EXPLODES_1X1;
7616
7617       SET_PROPERTY(element, EP_CAN_EXPLODE, (EXPLODES_BY_FIRE(element) ||
7618                                              EXPLODES_SMASHED(element) ||
7619                                              EXPLODES_IMPACT(element)));
7620     }
7621   }
7622
7623   // correct previously hard-coded move delay values for maze runner style
7624   if (level->game_version < VERSION_IDENT(3,1,1,0))
7625   {
7626     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7627     {
7628       int element = EL_CUSTOM_START + i;
7629
7630       if (element_info[element].move_pattern & MV_MAZE_RUNNER_STYLE)
7631       {
7632         // previously hard-coded and therefore ignored
7633         element_info[element].move_delay_fixed = 9;
7634         element_info[element].move_delay_random = 0;
7635       }
7636     }
7637   }
7638
7639   // set some other uninitialized values of custom elements in older levels
7640   if (level->game_version < VERSION_IDENT(3,1,0,0))
7641   {
7642     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7643     {
7644       int element = EL_CUSTOM_START + i;
7645
7646       element_info[element].access_direction = MV_ALL_DIRECTIONS;
7647
7648       element_info[element].explosion_delay = 17;
7649       element_info[element].ignition_delay = 8;
7650     }
7651   }
7652
7653   // set mouse click change events to work for left/middle/right mouse button
7654   if (level->game_version < VERSION_IDENT(4,2,3,0))
7655   {
7656     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
7657     {
7658       int element = EL_CUSTOM_START + i;
7659       struct ElementInfo *ei = &element_info[element];
7660
7661       for (j = 0; j < ei->num_change_pages; j++)
7662       {
7663         struct ElementChangeInfo *change = &ei->change_page[j];
7664
7665         if (change->has_event[CE_CLICKED_BY_MOUSE] ||
7666             change->has_event[CE_PRESSED_BY_MOUSE] ||
7667             change->has_event[CE_MOUSE_CLICKED_ON_X] ||
7668             change->has_event[CE_MOUSE_PRESSED_ON_X])
7669           change->trigger_side = CH_SIDE_ANY;
7670       }
7671     }
7672   }
7673 }
7674
7675 static void LoadLevel_InitElements(struct LevelInfo *level)
7676 {
7677   LoadLevel_InitStandardElements(level);
7678
7679   if (level->file_has_custom_elements)
7680     LoadLevel_InitCustomElements(level);
7681
7682   // initialize element properties for level editor etc.
7683   InitElementPropertiesEngine(level->game_version);
7684   InitElementPropertiesGfxElement();
7685 }
7686
7687 static void LoadLevel_InitPlayfield(struct LevelInfo *level)
7688 {
7689   int x, y;
7690
7691   // map elements that have changed in newer versions
7692   for (y = 0; y < level->fieldy; y++)
7693     for (x = 0; x < level->fieldx; x++)
7694       level->field[x][y] = getMappedElementByVersion(level->field[x][y],
7695                                                      level->game_version);
7696
7697   // clear unused playfield data (nicer if level gets resized in editor)
7698   for (x = 0; x < MAX_LEV_FIELDX; x++)
7699     for (y = 0; y < MAX_LEV_FIELDY; y++)
7700       if (x >= level->fieldx || y >= level->fieldy)
7701         level->field[x][y] = EL_EMPTY;
7702
7703   // copy elements to runtime playfield array
7704   for (x = 0; x < MAX_LEV_FIELDX; x++)
7705     for (y = 0; y < MAX_LEV_FIELDY; y++)
7706       Tile[x][y] = level->field[x][y];
7707
7708   // initialize level size variables for faster access
7709   lev_fieldx = level->fieldx;
7710   lev_fieldy = level->fieldy;
7711
7712   // determine border element for this level
7713   if (level->file_info.type == LEVEL_FILE_TYPE_DC)
7714     BorderElement = EL_EMPTY;   // (in editor, SetBorderElement() is used)
7715   else
7716     SetBorderElement();
7717 }
7718
7719 static void LoadLevel_InitNativeEngines(struct LevelInfo *level)
7720 {
7721   struct LevelFileInfo *level_file_info = &level->file_info;
7722
7723   if (level_file_info->type == LEVEL_FILE_TYPE_RND)
7724     CopyNativeLevel_RND_to_Native(level);
7725 }
7726
7727 static void LoadLevelTemplate_LoadAndInit(void)
7728 {
7729   LoadLevelFromFileInfo(&level_template, &level_template.file_info, FALSE);
7730
7731   LoadLevel_InitVersion(&level_template);
7732   LoadLevel_InitElements(&level_template);
7733   LoadLevel_InitSettings(&level_template);
7734
7735   ActivateLevelTemplate();
7736 }
7737
7738 void LoadLevelTemplate(int nr)
7739 {
7740   if (!fileExists(getGlobalLevelTemplateFilename()))
7741   {
7742     Warn("no level template found for this level");
7743
7744     return;
7745   }
7746
7747   setLevelFileInfo(&level_template.file_info, nr);
7748
7749   LoadLevelTemplate_LoadAndInit();
7750 }
7751
7752 static void LoadNetworkLevelTemplate(struct NetworkLevelInfo *network_level)
7753 {
7754   copyLevelFileInfo(&network_level->tmpl_info, &level_template.file_info);
7755
7756   LoadLevelTemplate_LoadAndInit();
7757 }
7758
7759 static void LoadLevel_LoadAndInit(struct NetworkLevelInfo *network_level)
7760 {
7761   LoadLevelFromFileInfo(&level, &level.file_info, FALSE);
7762
7763   if (level.use_custom_template)
7764   {
7765     if (network_level != NULL)
7766       LoadNetworkLevelTemplate(network_level);
7767     else
7768       LoadLevelTemplate(-1);
7769   }
7770
7771   LoadLevel_InitVersion(&level);
7772   LoadLevel_InitElements(&level);
7773   LoadLevel_InitPlayfield(&level);
7774   LoadLevel_InitSettings(&level);
7775
7776   LoadLevel_InitNativeEngines(&level);
7777 }
7778
7779 void LoadLevel(int nr)
7780 {
7781   SetLevelSetInfo(leveldir_current->identifier, nr);
7782
7783   setLevelFileInfo(&level.file_info, nr);
7784
7785   LoadLevel_LoadAndInit(NULL);
7786 }
7787
7788 void LoadLevelInfoOnly(int nr)
7789 {
7790   setLevelFileInfo(&level.file_info, nr);
7791
7792   LoadLevelFromFileInfo(&level, &level.file_info, TRUE);
7793 }
7794
7795 void LoadNetworkLevel(struct NetworkLevelInfo *network_level)
7796 {
7797   SetLevelSetInfo(network_level->leveldir_identifier,
7798                   network_level->file_info.nr);
7799
7800   copyLevelFileInfo(&network_level->file_info, &level.file_info);
7801
7802   LoadLevel_LoadAndInit(network_level);
7803 }
7804
7805 static int SaveLevel_VERS(FILE *file, struct LevelInfo *level)
7806 {
7807   int chunk_size = 0;
7808
7809   chunk_size += putFileVersion(file, level->file_version);
7810   chunk_size += putFileVersion(file, level->game_version);
7811
7812   return chunk_size;
7813 }
7814
7815 static int SaveLevel_DATE(FILE *file, struct LevelInfo *level)
7816 {
7817   int chunk_size = 0;
7818
7819   chunk_size += putFile16BitBE(file, level->creation_date.year);
7820   chunk_size += putFile8Bit(file,    level->creation_date.month);
7821   chunk_size += putFile8Bit(file,    level->creation_date.day);
7822
7823   return chunk_size;
7824 }
7825
7826 #if ENABLE_HISTORIC_CHUNKS
7827 static void SaveLevel_HEAD(FILE *file, struct LevelInfo *level)
7828 {
7829   int i, x, y;
7830
7831   putFile8Bit(file, level->fieldx);
7832   putFile8Bit(file, level->fieldy);
7833
7834   putFile16BitBE(file, level->time);
7835   putFile16BitBE(file, level->gems_needed);
7836
7837   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7838     putFile8Bit(file, level->name[i]);
7839
7840   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
7841     putFile8Bit(file, level->score[i]);
7842
7843   for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
7844     for (y = 0; y < 3; y++)
7845       for (x = 0; x < 3; x++)
7846         putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
7847                            level->yamyam_content[i].e[x][y]));
7848   putFile8Bit(file, level->amoeba_speed);
7849   putFile8Bit(file, level->time_magic_wall);
7850   putFile8Bit(file, level->time_wheel);
7851   putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
7852                      level->amoeba_content));
7853   putFile8Bit(file, (level->initial_player_stepsize == STEPSIZE_FAST ? 1 : 0));
7854   putFile8Bit(file, (level->initial_gravity ? 1 : 0));
7855   putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
7856   putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
7857
7858   putFile8Bit(file, (level->use_custom_template ? 1 : 0));
7859
7860   putFile8Bit(file, (level->block_last_field ? 1 : 0));
7861   putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
7862   putFile32BitBE(file, level->can_move_into_acid_bits);
7863   putFile8Bit(file, level->dont_collide_with_bits);
7864
7865   putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
7866   putFile8Bit(file, (level->use_step_counter ? 1 : 0));
7867
7868   putFile8Bit(file, (level->instant_relocation ? 1 : 0));
7869   putFile8Bit(file, (level->can_pass_to_walkable ? 1 : 0));
7870   putFile8Bit(file, (level->grow_into_diggable ? 1 : 0));
7871
7872   putFile8Bit(file, level->game_engine_type);
7873
7874   WriteUnusedBytesToFile(file, LEVEL_CHUNK_HEAD_UNUSED);
7875 }
7876 #endif
7877
7878 static int SaveLevel_NAME(FILE *file, struct LevelInfo *level)
7879 {
7880   int chunk_size = 0;
7881   int i;
7882
7883   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
7884     chunk_size += putFile8Bit(file, level->name[i]);
7885
7886   return chunk_size;
7887 }
7888
7889 static int SaveLevel_AUTH(FILE *file, struct LevelInfo *level)
7890 {
7891   int chunk_size = 0;
7892   int i;
7893
7894   for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
7895     chunk_size += putFile8Bit(file, level->author[i]);
7896
7897   return chunk_size;
7898 }
7899
7900 #if ENABLE_HISTORIC_CHUNKS
7901 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7902 {
7903   int chunk_size = 0;
7904   int x, y;
7905
7906   for (y = 0; y < level->fieldy; y++)
7907     for (x = 0; x < level->fieldx; x++)
7908       if (level->encoding_16bit_field)
7909         chunk_size += putFile16BitBE(file, level->field[x][y]);
7910       else
7911         chunk_size += putFile8Bit(file, level->field[x][y]);
7912
7913   return chunk_size;
7914 }
7915 #endif
7916
7917 static int SaveLevel_BODY(FILE *file, struct LevelInfo *level)
7918 {
7919   int chunk_size = 0;
7920   int x, y;
7921
7922   for (y = 0; y < level->fieldy; y++) 
7923     for (x = 0; x < level->fieldx; x++) 
7924       chunk_size += putFile16BitBE(file, level->field[x][y]);
7925
7926   return chunk_size;
7927 }
7928
7929 #if ENABLE_HISTORIC_CHUNKS
7930 static void SaveLevel_CONT(FILE *file, struct LevelInfo *level)
7931 {
7932   int i, x, y;
7933
7934   putFile8Bit(file, EL_YAMYAM);
7935   putFile8Bit(file, level->num_yamyam_contents);
7936   putFile8Bit(file, 0);
7937   putFile8Bit(file, 0);
7938
7939   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7940     for (y = 0; y < 3; y++)
7941       for (x = 0; x < 3; x++)
7942         if (level->encoding_16bit_field)
7943           putFile16BitBE(file, level->yamyam_content[i].e[x][y]);
7944         else
7945           putFile8Bit(file, level->yamyam_content[i].e[x][y]);
7946 }
7947 #endif
7948
7949 #if ENABLE_HISTORIC_CHUNKS
7950 static void SaveLevel_CNT2(FILE *file, struct LevelInfo *level, int element)
7951 {
7952   int i, x, y;
7953   int num_contents, content_xsize, content_ysize;
7954   int content_array[MAX_ELEMENT_CONTENTS][3][3];
7955
7956   if (element == EL_YAMYAM)
7957   {
7958     num_contents = level->num_yamyam_contents;
7959     content_xsize = 3;
7960     content_ysize = 3;
7961
7962     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7963       for (y = 0; y < 3; y++)
7964         for (x = 0; x < 3; x++)
7965           content_array[i][x][y] = level->yamyam_content[i].e[x][y];
7966   }
7967   else if (element == EL_BD_AMOEBA)
7968   {
7969     num_contents = 1;
7970     content_xsize = 1;
7971     content_ysize = 1;
7972
7973     for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7974       for (y = 0; y < 3; y++)
7975         for (x = 0; x < 3; x++)
7976           content_array[i][x][y] = EL_EMPTY;
7977     content_array[0][0][0] = level->amoeba_content;
7978   }
7979   else
7980   {
7981     // chunk header already written -- write empty chunk data
7982     WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_SIZE);
7983
7984     Warn("cannot save content for element '%d'", element);
7985
7986     return;
7987   }
7988
7989   putFile16BitBE(file, element);
7990   putFile8Bit(file, num_contents);
7991   putFile8Bit(file, content_xsize);
7992   putFile8Bit(file, content_ysize);
7993
7994   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
7995
7996   for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
7997     for (y = 0; y < 3; y++)
7998       for (x = 0; x < 3; x++)
7999         putFile16BitBE(file, content_array[i][x][y]);
8000 }
8001 #endif
8002
8003 #if ENABLE_HISTORIC_CHUNKS
8004 static int SaveLevel_CNT3(FILE *file, struct LevelInfo *level, int element)
8005 {
8006   int envelope_nr = element - EL_ENVELOPE_1;
8007   int envelope_len = strlen(level->envelope_text[envelope_nr]) + 1;
8008   int chunk_size = 0;
8009   int i;
8010
8011   chunk_size += putFile16BitBE(file, element);
8012   chunk_size += putFile16BitBE(file, envelope_len);
8013   chunk_size += putFile8Bit(file, level->envelope_xsize[envelope_nr]);
8014   chunk_size += putFile8Bit(file, level->envelope_ysize[envelope_nr]);
8015
8016   WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
8017   chunk_size += LEVEL_CHUNK_CNT3_UNUSED;
8018
8019   for (i = 0; i < envelope_len; i++)
8020     chunk_size += putFile8Bit(file, level->envelope_text[envelope_nr][i]);
8021
8022   return chunk_size;
8023 }
8024 #endif
8025
8026 #if ENABLE_HISTORIC_CHUNKS
8027 static void SaveLevel_CUS1(FILE *file, struct LevelInfo *level,
8028                            int num_changed_custom_elements)
8029 {
8030   int i, check = 0;
8031
8032   putFile16BitBE(file, num_changed_custom_elements);
8033
8034   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8035   {
8036     int element = EL_CUSTOM_START + i;
8037
8038     struct ElementInfo *ei = &element_info[element];
8039
8040     if (ei->properties[EP_BITFIELD_BASE_NR] != EP_BITMASK_DEFAULT)
8041     {
8042       if (check < num_changed_custom_elements)
8043       {
8044         putFile16BitBE(file, element);
8045         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8046       }
8047
8048       check++;
8049     }
8050   }
8051
8052   if (check != num_changed_custom_elements)     // should not happen
8053     Warn("inconsistent number of custom element properties");
8054 }
8055 #endif
8056
8057 #if ENABLE_HISTORIC_CHUNKS
8058 static void SaveLevel_CUS2(FILE *file, struct LevelInfo *level,
8059                            int num_changed_custom_elements)
8060 {
8061   int i, check = 0;
8062
8063   putFile16BitBE(file, num_changed_custom_elements);
8064
8065   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8066   {
8067     int element = EL_CUSTOM_START + i;
8068
8069     if (element_info[element].change->target_element != EL_EMPTY_SPACE)
8070     {
8071       if (check < num_changed_custom_elements)
8072       {
8073         putFile16BitBE(file, element);
8074         putFile16BitBE(file, element_info[element].change->target_element);
8075       }
8076
8077       check++;
8078     }
8079   }
8080
8081   if (check != num_changed_custom_elements)     // should not happen
8082     Warn("inconsistent number of custom target elements");
8083 }
8084 #endif
8085
8086 #if ENABLE_HISTORIC_CHUNKS
8087 static void SaveLevel_CUS3(FILE *file, struct LevelInfo *level,
8088                            int num_changed_custom_elements)
8089 {
8090   int i, j, x, y, check = 0;
8091
8092   putFile16BitBE(file, num_changed_custom_elements);
8093
8094   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8095   {
8096     int element = EL_CUSTOM_START + i;
8097     struct ElementInfo *ei = &element_info[element];
8098
8099     if (ei->modified_settings)
8100     {
8101       if (check < num_changed_custom_elements)
8102       {
8103         putFile16BitBE(file, element);
8104
8105         for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
8106           putFile8Bit(file, ei->description[j]);
8107
8108         putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8109
8110         // some free bytes for future properties and padding
8111         WriteUnusedBytesToFile(file, 7);
8112
8113         putFile8Bit(file, ei->use_gfx_element);
8114         putFile16BitBE(file, ei->gfx_element_initial);
8115
8116         putFile8Bit(file, ei->collect_score_initial);
8117         putFile8Bit(file, ei->collect_count_initial);
8118
8119         putFile16BitBE(file, ei->push_delay_fixed);
8120         putFile16BitBE(file, ei->push_delay_random);
8121         putFile16BitBE(file, ei->move_delay_fixed);
8122         putFile16BitBE(file, ei->move_delay_random);
8123
8124         putFile16BitBE(file, ei->move_pattern);
8125         putFile8Bit(file, ei->move_direction_initial);
8126         putFile8Bit(file, ei->move_stepsize);
8127
8128         for (y = 0; y < 3; y++)
8129           for (x = 0; x < 3; x++)
8130             putFile16BitBE(file, ei->content.e[x][y]);
8131
8132         putFile32BitBE(file, ei->change->events);
8133
8134         putFile16BitBE(file, ei->change->target_element);
8135
8136         putFile16BitBE(file, ei->change->delay_fixed);
8137         putFile16BitBE(file, ei->change->delay_random);
8138         putFile16BitBE(file, ei->change->delay_frames);
8139
8140         putFile16BitBE(file, ei->change->initial_trigger_element);
8141
8142         putFile8Bit(file, ei->change->explode);
8143         putFile8Bit(file, ei->change->use_target_content);
8144         putFile8Bit(file, ei->change->only_if_complete);
8145         putFile8Bit(file, ei->change->use_random_replace);
8146
8147         putFile8Bit(file, ei->change->random_percentage);
8148         putFile8Bit(file, ei->change->replace_when);
8149
8150         for (y = 0; y < 3; y++)
8151           for (x = 0; x < 3; x++)
8152             putFile16BitBE(file, ei->change->content.e[x][y]);
8153
8154         putFile8Bit(file, ei->slippery_type);
8155
8156         // some free bytes for future properties and padding
8157         WriteUnusedBytesToFile(file, LEVEL_CPART_CUS3_UNUSED);
8158       }
8159
8160       check++;
8161     }
8162   }
8163
8164   if (check != num_changed_custom_elements)     // should not happen
8165     Warn("inconsistent number of custom element properties");
8166 }
8167 #endif
8168
8169 #if ENABLE_HISTORIC_CHUNKS
8170 static void SaveLevel_CUS4(FILE *file, struct LevelInfo *level, int element)
8171 {
8172   struct ElementInfo *ei = &element_info[element];
8173   int i, j, x, y;
8174
8175   // ---------- custom element base property values (96 bytes) ----------------
8176
8177   putFile16BitBE(file, element);
8178
8179   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8180     putFile8Bit(file, ei->description[i]);
8181
8182   putFile32BitBE(file, ei->properties[EP_BITFIELD_BASE_NR]);
8183
8184   WriteUnusedBytesToFile(file, 4);      // reserved for more base properties
8185
8186   putFile8Bit(file, ei->num_change_pages);
8187
8188   putFile16BitBE(file, ei->ce_value_fixed_initial);
8189   putFile16BitBE(file, ei->ce_value_random_initial);
8190   putFile8Bit(file, ei->use_last_ce_value);
8191
8192   putFile8Bit(file, ei->use_gfx_element);
8193   putFile16BitBE(file, ei->gfx_element_initial);
8194
8195   putFile8Bit(file, ei->collect_score_initial);
8196   putFile8Bit(file, ei->collect_count_initial);
8197
8198   putFile8Bit(file, ei->drop_delay_fixed);
8199   putFile8Bit(file, ei->push_delay_fixed);
8200   putFile8Bit(file, ei->drop_delay_random);
8201   putFile8Bit(file, ei->push_delay_random);
8202   putFile16BitBE(file, ei->move_delay_fixed);
8203   putFile16BitBE(file, ei->move_delay_random);
8204
8205   // bits 0 - 15 of "move_pattern" ...
8206   putFile16BitBE(file, ei->move_pattern & 0xffff);
8207   putFile8Bit(file, ei->move_direction_initial);
8208   putFile8Bit(file, ei->move_stepsize);
8209
8210   putFile8Bit(file, ei->slippery_type);
8211
8212   for (y = 0; y < 3; y++)
8213     for (x = 0; x < 3; x++)
8214       putFile16BitBE(file, ei->content.e[x][y]);
8215
8216   putFile16BitBE(file, ei->move_enter_element);
8217   putFile16BitBE(file, ei->move_leave_element);
8218   putFile8Bit(file, ei->move_leave_type);
8219
8220   // ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible)
8221   putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
8222
8223   putFile8Bit(file, ei->access_direction);
8224
8225   putFile8Bit(file, ei->explosion_delay);
8226   putFile8Bit(file, ei->ignition_delay);
8227   putFile8Bit(file, ei->explosion_type);
8228
8229   // some free bytes for future custom property values and padding
8230   WriteUnusedBytesToFile(file, 1);
8231
8232   // ---------- change page property values (48 bytes) ------------------------
8233
8234   for (i = 0; i < ei->num_change_pages; i++)
8235   {
8236     struct ElementChangeInfo *change = &ei->change_page[i];
8237     unsigned int event_bits;
8238
8239     // bits 0 - 31 of "has_event[]" ...
8240     event_bits = 0;
8241     for (j = 0; j < MIN(NUM_CHANGE_EVENTS, 32); j++)
8242       if (change->has_event[j])
8243         event_bits |= (1u << j);
8244     putFile32BitBE(file, event_bits);
8245
8246     putFile16BitBE(file, change->target_element);
8247
8248     putFile16BitBE(file, change->delay_fixed);
8249     putFile16BitBE(file, change->delay_random);
8250     putFile16BitBE(file, change->delay_frames);
8251
8252     putFile16BitBE(file, change->initial_trigger_element);
8253
8254     putFile8Bit(file, change->explode);
8255     putFile8Bit(file, change->use_target_content);
8256     putFile8Bit(file, change->only_if_complete);
8257     putFile8Bit(file, change->use_random_replace);
8258
8259     putFile8Bit(file, change->random_percentage);
8260     putFile8Bit(file, change->replace_when);
8261
8262     for (y = 0; y < 3; y++)
8263       for (x = 0; x < 3; x++)
8264         putFile16BitBE(file, change->target_content.e[x][y]);
8265
8266     putFile8Bit(file, change->can_change);
8267
8268     putFile8Bit(file, change->trigger_side);
8269
8270     putFile8Bit(file, change->trigger_player);
8271     putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
8272                        log_2(change->trigger_page)));
8273
8274     putFile8Bit(file, change->has_action);
8275     putFile8Bit(file, change->action_type);
8276     putFile8Bit(file, change->action_mode);
8277     putFile16BitBE(file, change->action_arg);
8278
8279     // ... bits 32 - 39 of "has_event[]" (not nice, but downward compatible)
8280     event_bits = 0;
8281     for (j = 32; j < NUM_CHANGE_EVENTS; j++)
8282       if (change->has_event[j])
8283         event_bits |= (1u << (j - 32));
8284     putFile8Bit(file, event_bits);
8285   }
8286 }
8287 #endif
8288
8289 #if ENABLE_HISTORIC_CHUNKS
8290 static void SaveLevel_GRP1(FILE *file, struct LevelInfo *level, int element)
8291 {
8292   struct ElementInfo *ei = &element_info[element];
8293   struct ElementGroupInfo *group = ei->group;
8294   int i;
8295
8296   putFile16BitBE(file, element);
8297
8298   for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
8299     putFile8Bit(file, ei->description[i]);
8300
8301   putFile8Bit(file, group->num_elements);
8302
8303   putFile8Bit(file, ei->use_gfx_element);
8304   putFile16BitBE(file, ei->gfx_element_initial);
8305
8306   putFile8Bit(file, group->choice_mode);
8307
8308   // some free bytes for future values and padding
8309   WriteUnusedBytesToFile(file, 3);
8310
8311   for (i = 0; i < MAX_ELEMENTS_IN_GROUP; i++)
8312     putFile16BitBE(file, group->element[i]);
8313 }
8314 #endif
8315
8316 static int SaveLevel_MicroChunk(FILE *file, struct LevelFileConfigInfo *entry,
8317                                 boolean write_element)
8318 {
8319   int save_type = entry->save_type;
8320   int data_type = entry->data_type;
8321   int conf_type = entry->conf_type;
8322   int byte_mask = conf_type & CONF_MASK_BYTES;
8323   int element = entry->element;
8324   int default_value = entry->default_value;
8325   int num_bytes = 0;
8326   boolean modified = FALSE;
8327
8328   if (byte_mask != CONF_MASK_MULTI_BYTES)
8329   {
8330     void *value_ptr = entry->value;
8331     int value = (data_type == TYPE_BOOLEAN ? *(boolean *)value_ptr :
8332                  *(int *)value_ptr);
8333
8334     // check if any settings have been modified before saving them
8335     if (value != default_value)
8336       modified = TRUE;
8337
8338     // do not save if explicitly told or if unmodified default settings
8339     if ((save_type == SAVE_CONF_NEVER) ||
8340         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8341       return 0;
8342
8343     if (write_element)
8344       num_bytes += putFile16BitBE(file, element);
8345
8346     num_bytes += putFile8Bit(file, conf_type);
8347     num_bytes += (byte_mask == CONF_MASK_1_BYTE ? putFile8Bit   (file, value) :
8348                   byte_mask == CONF_MASK_2_BYTE ? putFile16BitBE(file, value) :
8349                   byte_mask == CONF_MASK_4_BYTE ? putFile32BitBE(file, value) :
8350                   0);
8351   }
8352   else if (data_type == TYPE_STRING)
8353   {
8354     char *default_string = entry->default_string;
8355     char *string = (char *)(entry->value);
8356     int string_length = strlen(string);
8357     int i;
8358
8359     // check if any settings have been modified before saving them
8360     if (!strEqual(string, default_string))
8361       modified = TRUE;
8362
8363     // do not save if explicitly told or if unmodified default settings
8364     if ((save_type == SAVE_CONF_NEVER) ||
8365         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8366       return 0;
8367
8368     if (write_element)
8369       num_bytes += putFile16BitBE(file, element);
8370
8371     num_bytes += putFile8Bit(file, conf_type);
8372     num_bytes += putFile16BitBE(file, string_length);
8373
8374     for (i = 0; i < string_length; i++)
8375       num_bytes += putFile8Bit(file, string[i]);
8376   }
8377   else if (data_type == TYPE_ELEMENT_LIST)
8378   {
8379     int *element_array = (int *)(entry->value);
8380     int num_elements = *(int *)(entry->num_entities);
8381     int i;
8382
8383     // check if any settings have been modified before saving them
8384     for (i = 0; i < num_elements; i++)
8385       if (element_array[i] != default_value)
8386         modified = TRUE;
8387
8388     // do not save if explicitly told or if unmodified default settings
8389     if ((save_type == SAVE_CONF_NEVER) ||
8390         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8391       return 0;
8392
8393     if (write_element)
8394       num_bytes += putFile16BitBE(file, element);
8395
8396     num_bytes += putFile8Bit(file, conf_type);
8397     num_bytes += putFile16BitBE(file, num_elements * CONF_ELEMENT_NUM_BYTES);
8398
8399     for (i = 0; i < num_elements; i++)
8400       num_bytes += putFile16BitBE(file, element_array[i]);
8401   }
8402   else if (data_type == TYPE_CONTENT_LIST)
8403   {
8404     struct Content *content = (struct Content *)(entry->value);
8405     int num_contents = *(int *)(entry->num_entities);
8406     int i, x, y;
8407
8408     // check if any settings have been modified before saving them
8409     for (i = 0; i < num_contents; i++)
8410       for (y = 0; y < 3; y++)
8411         for (x = 0; x < 3; x++)
8412           if (content[i].e[x][y] != default_value)
8413             modified = TRUE;
8414
8415     // do not save if explicitly told or if unmodified default settings
8416     if ((save_type == SAVE_CONF_NEVER) ||
8417         (save_type == SAVE_CONF_WHEN_CHANGED && !modified))
8418       return 0;
8419
8420     if (write_element)
8421       num_bytes += putFile16BitBE(file, element);
8422
8423     num_bytes += putFile8Bit(file, conf_type);
8424     num_bytes += putFile16BitBE(file, num_contents * CONF_CONTENT_NUM_BYTES);
8425
8426     for (i = 0; i < num_contents; i++)
8427       for (y = 0; y < 3; y++)
8428         for (x = 0; x < 3; x++)
8429           num_bytes += putFile16BitBE(file, content[i].e[x][y]);
8430   }
8431
8432   return num_bytes;
8433 }
8434
8435 static int SaveLevel_INFO(FILE *file, struct LevelInfo *level)
8436 {
8437   int chunk_size = 0;
8438   int i;
8439
8440   li = *level;          // copy level data into temporary buffer
8441
8442   for (i = 0; chunk_config_INFO[i].data_type != -1; i++)
8443     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_INFO[i], FALSE);
8444
8445   return chunk_size;
8446 }
8447
8448 static int SaveLevel_ELEM(FILE *file, struct LevelInfo *level)
8449 {
8450   int chunk_size = 0;
8451   int i;
8452
8453   li = *level;          // copy level data into temporary buffer
8454
8455   for (i = 0; chunk_config_ELEM[i].data_type != -1; i++)
8456     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_ELEM[i], TRUE);
8457
8458   return chunk_size;
8459 }
8460
8461 static int SaveLevel_NOTE(FILE *file, struct LevelInfo *level, int element)
8462 {
8463   int envelope_nr = element - EL_ENVELOPE_1;
8464   int chunk_size = 0;
8465   int i;
8466
8467   chunk_size += putFile16BitBE(file, element);
8468
8469   // copy envelope data into temporary buffer
8470   xx_envelope = level->envelope[envelope_nr];
8471
8472   for (i = 0; chunk_config_NOTE[i].data_type != -1; i++)
8473     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_NOTE[i], FALSE);
8474
8475   return chunk_size;
8476 }
8477
8478 static int SaveLevel_CUSX(FILE *file, struct LevelInfo *level, int element)
8479 {
8480   struct ElementInfo *ei = &element_info[element];
8481   int chunk_size = 0;
8482   int i, j;
8483
8484   chunk_size += putFile16BitBE(file, element);
8485
8486   xx_ei = *ei;          // copy element data into temporary buffer
8487
8488   // set default description string for this specific element
8489   strcpy(xx_default_description, getDefaultElementDescription(ei));
8490
8491   for (i = 0; chunk_config_CUSX_base[i].data_type != -1; i++)
8492     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_base[i], FALSE);
8493
8494   for (i = 0; i < ei->num_change_pages; i++)
8495   {
8496     struct ElementChangeInfo *change = &ei->change_page[i];
8497
8498     xx_current_change_page = i;
8499
8500     xx_change = *change;        // copy change data into temporary buffer
8501
8502     resetEventBits();
8503     setEventBitsFromEventFlags(change);
8504
8505     for (j = 0; chunk_config_CUSX_change[j].data_type != -1; j++)
8506       chunk_size += SaveLevel_MicroChunk(file, &chunk_config_CUSX_change[j],
8507                                          FALSE);
8508   }
8509
8510   return chunk_size;
8511 }
8512
8513 static int SaveLevel_GRPX(FILE *file, struct LevelInfo *level, int element)
8514 {
8515   struct ElementInfo *ei = &element_info[element];
8516   struct ElementGroupInfo *group = ei->group;
8517   int chunk_size = 0;
8518   int i;
8519
8520   chunk_size += putFile16BitBE(file, element);
8521
8522   xx_ei = *ei;          // copy element data into temporary buffer
8523   xx_group = *group;    // copy group data into temporary buffer
8524
8525   // set default description string for this specific element
8526   strcpy(xx_default_description, getDefaultElementDescription(ei));
8527
8528   for (i = 0; chunk_config_GRPX[i].data_type != -1; i++)
8529     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_GRPX[i], FALSE);
8530
8531   return chunk_size;
8532 }
8533
8534 static int SaveLevel_EMPX(FILE *file, struct LevelInfo *level, int element)
8535 {
8536   struct ElementInfo *ei = &element_info[element];
8537   int chunk_size = 0;
8538   int i;
8539
8540   chunk_size += putFile16BitBE(file, element);
8541
8542   xx_ei = *ei;          // copy element data into temporary buffer
8543
8544   for (i = 0; chunk_config_EMPX[i].data_type != -1; i++)
8545     chunk_size += SaveLevel_MicroChunk(file, &chunk_config_EMPX[i], FALSE);
8546
8547   return chunk_size;
8548 }
8549
8550 static void SaveLevelFromFilename(struct LevelInfo *level, char *filename,
8551                                   boolean save_as_template)
8552 {
8553   int chunk_size;
8554   int i;
8555   FILE *file;
8556
8557   if (!(file = fopen(filename, MODE_WRITE)))
8558   {
8559     Warn("cannot save level file '%s'", filename);
8560
8561     return;
8562   }
8563
8564   level->file_version = FILE_VERSION_ACTUAL;
8565   level->game_version = GAME_VERSION_ACTUAL;
8566
8567   level->creation_date = getCurrentDate();
8568
8569   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
8570   putFileChunkBE(file, "CAVE", CHUNK_SIZE_NONE);
8571
8572   chunk_size = SaveLevel_VERS(NULL, level);
8573   putFileChunkBE(file, "VERS", chunk_size);
8574   SaveLevel_VERS(file, level);
8575
8576   chunk_size = SaveLevel_DATE(NULL, level);
8577   putFileChunkBE(file, "DATE", chunk_size);
8578   SaveLevel_DATE(file, level);
8579
8580   chunk_size = SaveLevel_NAME(NULL, level);
8581   putFileChunkBE(file, "NAME", chunk_size);
8582   SaveLevel_NAME(file, level);
8583
8584   chunk_size = SaveLevel_AUTH(NULL, level);
8585   putFileChunkBE(file, "AUTH", chunk_size);
8586   SaveLevel_AUTH(file, level);
8587
8588   chunk_size = SaveLevel_INFO(NULL, level);
8589   putFileChunkBE(file, "INFO", chunk_size);
8590   SaveLevel_INFO(file, level);
8591
8592   chunk_size = SaveLevel_BODY(NULL, level);
8593   putFileChunkBE(file, "BODY", chunk_size);
8594   SaveLevel_BODY(file, level);
8595
8596   chunk_size = SaveLevel_ELEM(NULL, level);
8597   if (chunk_size > LEVEL_CHUNK_ELEM_UNCHANGED)          // save if changed
8598   {
8599     putFileChunkBE(file, "ELEM", chunk_size);
8600     SaveLevel_ELEM(file, level);
8601   }
8602
8603   for (i = 0; i < NUM_ENVELOPES; i++)
8604   {
8605     int element = EL_ENVELOPE_1 + i;
8606
8607     chunk_size = SaveLevel_NOTE(NULL, level, element);
8608     if (chunk_size > LEVEL_CHUNK_NOTE_UNCHANGED)        // save if changed
8609     {
8610       putFileChunkBE(file, "NOTE", chunk_size);
8611       SaveLevel_NOTE(file, level, element);
8612     }
8613   }
8614
8615   // if not using template level, check for non-default custom/group elements
8616   if (!level->use_custom_template || save_as_template)
8617   {
8618     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
8619     {
8620       int element = EL_CUSTOM_START + i;
8621
8622       chunk_size = SaveLevel_CUSX(NULL, level, element);
8623       if (chunk_size > LEVEL_CHUNK_CUSX_UNCHANGED)      // save if changed
8624       {
8625         putFileChunkBE(file, "CUSX", chunk_size);
8626         SaveLevel_CUSX(file, level, element);
8627       }
8628     }
8629
8630     for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
8631     {
8632       int element = EL_GROUP_START + i;
8633
8634       chunk_size = SaveLevel_GRPX(NULL, level, element);
8635       if (chunk_size > LEVEL_CHUNK_GRPX_UNCHANGED)      // save if changed
8636       {
8637         putFileChunkBE(file, "GRPX", chunk_size);
8638         SaveLevel_GRPX(file, level, element);
8639       }
8640     }
8641
8642     for (i = 0; i < NUM_EMPTY_ELEMENTS_ALL; i++)
8643     {
8644       int element = GET_EMPTY_ELEMENT(i);
8645
8646       chunk_size = SaveLevel_EMPX(NULL, level, element);
8647       if (chunk_size > LEVEL_CHUNK_EMPX_UNCHANGED)      // save if changed
8648       {
8649         putFileChunkBE(file, "EMPX", chunk_size);
8650         SaveLevel_EMPX(file, level, element);
8651       }
8652     }
8653   }
8654
8655   fclose(file);
8656
8657   SetFilePermissions(filename, PERMS_PRIVATE);
8658 }
8659
8660 void SaveLevel(int nr)
8661 {
8662   char *filename = getDefaultLevelFilename(nr);
8663
8664   SaveLevelFromFilename(&level, filename, FALSE);
8665 }
8666
8667 void SaveLevelTemplate(void)
8668 {
8669   char *filename = getLocalLevelTemplateFilename();
8670
8671   SaveLevelFromFilename(&level, filename, TRUE);
8672 }
8673
8674 boolean SaveLevelChecked(int nr)
8675 {
8676   char *filename = getDefaultLevelFilename(nr);
8677   boolean new_level = !fileExists(filename);
8678   boolean level_saved = FALSE;
8679
8680   if (new_level || Request("Save this level and kill the old?", REQ_ASK))
8681   {
8682     SaveLevel(nr);
8683
8684     if (new_level)
8685       Request("Level saved!", REQ_CONFIRM);
8686
8687     level_saved = TRUE;
8688   }
8689
8690   return level_saved;
8691 }
8692
8693 void DumpLevel(struct LevelInfo *level)
8694 {
8695   if (level->no_level_file || level->no_valid_file)
8696   {
8697     Warn("cannot dump -- no valid level file found");
8698
8699     return;
8700   }
8701
8702   PrintLine("-", 79);
8703   Print("Level xxx (file version %08d, game version %08d)\n",
8704         level->file_version, level->game_version);
8705   PrintLine("-", 79);
8706
8707   Print("Level author: '%s'\n", level->author);
8708   Print("Level title:  '%s'\n", level->name);
8709   Print("\n");
8710   Print("Playfield size: %d x %d\n", level->fieldx, level->fieldy);
8711   Print("\n");
8712   Print("Level time:  %d seconds\n", level->time);
8713   Print("Gems needed: %d\n", level->gems_needed);
8714   Print("\n");
8715   Print("Time for magic wall: %d seconds\n", level->time_magic_wall);
8716   Print("Time for wheel:      %d seconds\n", level->time_wheel);
8717   Print("Time for light:      %d seconds\n", level->time_light);
8718   Print("Time for timegate:   %d seconds\n", level->time_timegate);
8719   Print("\n");
8720   Print("Amoeba speed: %d\n", level->amoeba_speed);
8721   Print("\n");
8722
8723   Print("EM style slippery gems:      %s\n", (level->em_slippery_gems ? "yes" : "no"));
8724   Print("Player blocks last field:    %s\n", (level->block_last_field ? "yes" : "no"));
8725   Print("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
8726   Print("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
8727   Print("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
8728   Print("rate time over score: %s\n", (level->rate_time_over_score ? "yes" : "no"));
8729
8730   if (options.debug)
8731   {
8732     int i, j;
8733
8734     for (i = 0; i < NUM_ENVELOPES; i++)
8735     {
8736       char *text = level->envelope[i].text;
8737       int text_len = strlen(text);
8738       boolean has_text = FALSE;
8739
8740       for (j = 0; j < text_len; j++)
8741         if (text[j] != ' ' && text[j] != '\n')
8742           has_text = TRUE;
8743
8744       if (has_text)
8745       {
8746         Print("\n");
8747         Print("Envelope %d:\n'%s'\n", i + 1, text);
8748       }
8749     }
8750   }
8751
8752   PrintLine("-", 79);
8753 }
8754
8755 void DumpLevels(void)
8756 {
8757   static LevelDirTree *dumplevel_leveldir = NULL;
8758
8759   dumplevel_leveldir = getTreeInfoFromIdentifier(leveldir_first,
8760                                                  global.dumplevel_leveldir);
8761
8762   if (dumplevel_leveldir == NULL)
8763     Fail("no such level identifier: '%s'", global.dumplevel_leveldir);
8764
8765   if (global.dumplevel_level_nr < dumplevel_leveldir->first_level ||
8766       global.dumplevel_level_nr > dumplevel_leveldir->last_level)
8767     Fail("no such level number: %d", global.dumplevel_level_nr);
8768
8769   leveldir_current = dumplevel_leveldir;
8770
8771   LoadLevel(global.dumplevel_level_nr);
8772   DumpLevel(&level);
8773
8774   CloseAllAndExit(0);
8775 }
8776
8777
8778 // ============================================================================
8779 // tape file functions
8780 // ============================================================================
8781
8782 static void setTapeInfoToDefaults(void)
8783 {
8784   int i;
8785
8786   // always start with reliable default values (empty tape)
8787   TapeErase();
8788
8789   // default values (also for pre-1.2 tapes) with only the first player
8790   tape.player_participates[0] = TRUE;
8791   for (i = 1; i < MAX_PLAYERS; i++)
8792     tape.player_participates[i] = FALSE;
8793
8794   // at least one (default: the first) player participates in every tape
8795   tape.num_participating_players = 1;
8796
8797   tape.property_bits = TAPE_PROPERTY_NONE;
8798
8799   tape.level_nr = level_nr;
8800   tape.counter = 0;
8801   tape.changed = FALSE;
8802   tape.solved = FALSE;
8803
8804   tape.recording = FALSE;
8805   tape.playing = FALSE;
8806   tape.pausing = FALSE;
8807
8808   tape.scr_fieldx = SCR_FIELDX_DEFAULT;
8809   tape.scr_fieldy = SCR_FIELDY_DEFAULT;
8810
8811   tape.no_info_chunk = TRUE;
8812   tape.no_valid_file = FALSE;
8813 }
8814
8815 static int getTapePosSize(struct TapeInfo *tape)
8816 {
8817   int tape_pos_size = 0;
8818
8819   if (tape->use_key_actions)
8820     tape_pos_size += tape->num_participating_players;
8821
8822   if (tape->use_mouse_actions)
8823     tape_pos_size += 3;         // x and y position and mouse button mask
8824
8825   tape_pos_size += 1;           // tape action delay value
8826
8827   return tape_pos_size;
8828 }
8829
8830 static void setTapeActionFlags(struct TapeInfo *tape, int value)
8831 {
8832   tape->use_key_actions = FALSE;
8833   tape->use_mouse_actions = FALSE;
8834
8835   if (value != TAPE_USE_MOUSE_ACTIONS_ONLY)
8836     tape->use_key_actions = TRUE;
8837
8838   if (value != TAPE_USE_KEY_ACTIONS_ONLY)
8839     tape->use_mouse_actions = TRUE;
8840 }
8841
8842 static int getTapeActionValue(struct TapeInfo *tape)
8843 {
8844   return (tape->use_key_actions &&
8845           tape->use_mouse_actions ? TAPE_USE_KEY_AND_MOUSE_ACTIONS :
8846           tape->use_key_actions   ? TAPE_USE_KEY_ACTIONS_ONLY :
8847           tape->use_mouse_actions ? TAPE_USE_MOUSE_ACTIONS_ONLY :
8848           TAPE_ACTIONS_DEFAULT);
8849 }
8850
8851 static int LoadTape_VERS(File *file, int chunk_size, struct TapeInfo *tape)
8852 {
8853   tape->file_version = getFileVersion(file);
8854   tape->game_version = getFileVersion(file);
8855
8856   return chunk_size;
8857 }
8858
8859 static int LoadTape_HEAD(File *file, int chunk_size, struct TapeInfo *tape)
8860 {
8861   int i;
8862
8863   tape->random_seed = getFile32BitBE(file);
8864   tape->date        = getFile32BitBE(file);
8865   tape->length      = getFile32BitBE(file);
8866
8867   // read header fields that are new since version 1.2
8868   if (tape->file_version >= FILE_VERSION_1_2)
8869   {
8870     byte store_participating_players = getFile8Bit(file);
8871     int engine_version;
8872
8873     // since version 1.2, tapes store which players participate in the tape
8874     tape->num_participating_players = 0;
8875     for (i = 0; i < MAX_PLAYERS; i++)
8876     {
8877       tape->player_participates[i] = FALSE;
8878
8879       if (store_participating_players & (1 << i))
8880       {
8881         tape->player_participates[i] = TRUE;
8882         tape->num_participating_players++;
8883       }
8884     }
8885
8886     setTapeActionFlags(tape, getFile8Bit(file));
8887
8888     tape->property_bits = getFile8Bit(file);
8889     tape->solved = getFile8Bit(file);
8890
8891     engine_version = getFileVersion(file);
8892     if (engine_version > 0)
8893       tape->engine_version = engine_version;
8894     else
8895       tape->engine_version = tape->game_version;
8896   }
8897
8898   return chunk_size;
8899 }
8900
8901 static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
8902 {
8903   tape->scr_fieldx = getFile8Bit(file);
8904   tape->scr_fieldy = getFile8Bit(file);
8905
8906   return chunk_size;
8907 }
8908
8909 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
8910 {
8911   char *level_identifier = NULL;
8912   int level_identifier_size;
8913   int i;
8914
8915   tape->no_info_chunk = FALSE;
8916
8917   level_identifier_size = getFile16BitBE(file);
8918
8919   level_identifier = checked_malloc(level_identifier_size);
8920
8921   for (i = 0; i < level_identifier_size; i++)
8922     level_identifier[i] = getFile8Bit(file);
8923
8924   strncpy(tape->level_identifier, level_identifier, MAX_FILENAME_LEN);
8925   tape->level_identifier[MAX_FILENAME_LEN] = '\0';
8926
8927   checked_free(level_identifier);
8928
8929   tape->level_nr = getFile16BitBE(file);
8930
8931   chunk_size = 2 + level_identifier_size + 2;
8932
8933   return chunk_size;
8934 }
8935
8936 static int LoadTape_BODY(File *file, int chunk_size, struct TapeInfo *tape)
8937 {
8938   int i, j;
8939   int tape_pos_size = getTapePosSize(tape);
8940   int chunk_size_expected = tape_pos_size * tape->length;
8941
8942   if (chunk_size_expected != chunk_size)
8943   {
8944     ReadUnusedBytesFromFile(file, chunk_size);
8945     return chunk_size_expected;
8946   }
8947
8948   for (i = 0; i < tape->length; i++)
8949   {
8950     if (i >= MAX_TAPE_LEN)
8951     {
8952       Warn("tape truncated -- size exceeds maximum tape size %d",
8953             MAX_TAPE_LEN);
8954
8955       // tape too large; read and ignore remaining tape data from this chunk
8956       for (;i < tape->length; i++)
8957         ReadUnusedBytesFromFile(file, tape_pos_size);
8958
8959       break;
8960     }
8961
8962     if (tape->use_key_actions)
8963     {
8964       for (j = 0; j < MAX_PLAYERS; j++)
8965       {
8966         tape->pos[i].action[j] = MV_NONE;
8967
8968         if (tape->player_participates[j])
8969           tape->pos[i].action[j] = getFile8Bit(file);
8970       }
8971     }
8972
8973     if (tape->use_mouse_actions)
8974     {
8975       tape->pos[i].action[TAPE_ACTION_LX]     = getFile8Bit(file);
8976       tape->pos[i].action[TAPE_ACTION_LY]     = getFile8Bit(file);
8977       tape->pos[i].action[TAPE_ACTION_BUTTON] = getFile8Bit(file);
8978     }
8979
8980     tape->pos[i].delay = getFile8Bit(file);
8981
8982     if (tape->file_version == FILE_VERSION_1_0)
8983     {
8984       // eliminate possible diagonal moves in old tapes
8985       // this is only for backward compatibility
8986
8987       byte joy_dir[4] = { JOY_LEFT, JOY_RIGHT, JOY_UP, JOY_DOWN };
8988       byte action = tape->pos[i].action[0];
8989       int k, num_moves = 0;
8990
8991       for (k = 0; k < 4; k++)
8992       {
8993         if (action & joy_dir[k])
8994         {
8995           tape->pos[i + num_moves].action[0] = joy_dir[k];
8996           if (num_moves > 0)
8997             tape->pos[i + num_moves].delay = 0;
8998           num_moves++;
8999         }
9000       }
9001
9002       if (num_moves > 1)
9003       {
9004         num_moves--;
9005         i += num_moves;
9006         tape->length += num_moves;
9007       }
9008     }
9009     else if (tape->file_version < FILE_VERSION_2_0)
9010     {
9011       // convert pre-2.0 tapes to new tape format
9012
9013       if (tape->pos[i].delay > 1)
9014       {
9015         // action part
9016         tape->pos[i + 1] = tape->pos[i];
9017         tape->pos[i + 1].delay = 1;
9018
9019         // delay part
9020         for (j = 0; j < MAX_PLAYERS; j++)
9021           tape->pos[i].action[j] = MV_NONE;
9022         tape->pos[i].delay--;
9023
9024         i++;
9025         tape->length++;
9026       }
9027     }
9028
9029     if (checkEndOfFile(file))
9030       break;
9031   }
9032
9033   if (i != tape->length)
9034     chunk_size = tape_pos_size * i;
9035
9036   return chunk_size;
9037 }
9038
9039 static void LoadTape_SokobanSolution(char *filename)
9040 {
9041   File *file;
9042   int move_delay = TILESIZE / level.initial_player_stepsize[0];
9043
9044   if (!(file = openFile(filename, MODE_READ)))
9045   {
9046     tape.no_valid_file = TRUE;
9047
9048     return;
9049   }
9050
9051   while (!checkEndOfFile(file))
9052   {
9053     unsigned char c = getByteFromFile(file);
9054
9055     if (checkEndOfFile(file))
9056       break;
9057
9058     switch (c)
9059     {
9060       case 'u':
9061       case 'U':
9062         tape.pos[tape.length].action[0] = MV_UP;
9063         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9064         tape.length++;
9065         break;
9066
9067       case 'd':
9068       case 'D':
9069         tape.pos[tape.length].action[0] = MV_DOWN;
9070         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9071         tape.length++;
9072         break;
9073
9074       case 'l':
9075       case 'L':
9076         tape.pos[tape.length].action[0] = MV_LEFT;
9077         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9078         tape.length++;
9079         break;
9080
9081       case 'r':
9082       case 'R':
9083         tape.pos[tape.length].action[0] = MV_RIGHT;
9084         tape.pos[tape.length].delay = move_delay + (c < 'a' ? 2 : 0);
9085         tape.length++;
9086         break;
9087
9088       case '\n':
9089       case '\r':
9090       case '\t':
9091       case ' ':
9092         // ignore white-space characters
9093         break;
9094
9095       default:
9096         tape.no_valid_file = TRUE;
9097
9098         Warn("unsupported Sokoban solution file '%s' ['%d']", filename, c);
9099
9100         break;
9101     }
9102   }
9103
9104   closeFile(file);
9105
9106   if (tape.no_valid_file)
9107     return;
9108
9109   tape.length_frames  = GetTapeLengthFrames();
9110   tape.length_seconds = GetTapeLengthSeconds();
9111 }
9112
9113 void LoadTapeFromFilename(char *filename)
9114 {
9115   char cookie[MAX_LINE_LEN];
9116   char chunk_name[CHUNK_ID_LEN + 1];
9117   File *file;
9118   int chunk_size;
9119
9120   // always start with reliable default values
9121   setTapeInfoToDefaults();
9122
9123   if (strSuffix(filename, ".sln"))
9124   {
9125     LoadTape_SokobanSolution(filename);
9126
9127     return;
9128   }
9129
9130   if (!(file = openFile(filename, MODE_READ)))
9131   {
9132     tape.no_valid_file = TRUE;
9133
9134     return;
9135   }
9136
9137   getFileChunkBE(file, chunk_name, NULL);
9138   if (strEqual(chunk_name, "RND1"))
9139   {
9140     getFile32BitBE(file);               // not used
9141
9142     getFileChunkBE(file, chunk_name, NULL);
9143     if (!strEqual(chunk_name, "TAPE"))
9144     {
9145       tape.no_valid_file = TRUE;
9146
9147       Warn("unknown format of tape file '%s'", filename);
9148
9149       closeFile(file);
9150
9151       return;
9152     }
9153   }
9154   else  // check for pre-2.0 file format with cookie string
9155   {
9156     strcpy(cookie, chunk_name);
9157     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9158       cookie[4] = '\0';
9159     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9160       cookie[strlen(cookie) - 1] = '\0';
9161
9162     if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
9163     {
9164       tape.no_valid_file = TRUE;
9165
9166       Warn("unknown format of tape file '%s'", filename);
9167
9168       closeFile(file);
9169
9170       return;
9171     }
9172
9173     if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
9174     {
9175       tape.no_valid_file = TRUE;
9176
9177       Warn("unsupported version of tape file '%s'", filename);
9178
9179       closeFile(file);
9180
9181       return;
9182     }
9183
9184     // pre-2.0 tape files have no game version, so use file version here
9185     tape.game_version = tape.file_version;
9186   }
9187
9188   if (tape.file_version < FILE_VERSION_1_2)
9189   {
9190     // tape files from versions before 1.2.0 without chunk structure
9191     LoadTape_HEAD(file, TAPE_CHUNK_HEAD_SIZE, &tape);
9192     LoadTape_BODY(file, 2 * tape.length,      &tape);
9193   }
9194   else
9195   {
9196     static struct
9197     {
9198       char *name;
9199       int size;
9200       int (*loader)(File *, int, struct TapeInfo *);
9201     }
9202     chunk_info[] =
9203     {
9204       { "VERS", TAPE_CHUNK_VERS_SIZE,   LoadTape_VERS },
9205       { "HEAD", TAPE_CHUNK_HEAD_SIZE,   LoadTape_HEAD },
9206       { "SCRN", TAPE_CHUNK_SCRN_SIZE,   LoadTape_SCRN },
9207       { "INFO", -1,                     LoadTape_INFO },
9208       { "BODY", -1,                     LoadTape_BODY },
9209       {  NULL,  0,                      NULL }
9210     };
9211
9212     while (getFileChunkBE(file, chunk_name, &chunk_size))
9213     {
9214       int i = 0;
9215
9216       while (chunk_info[i].name != NULL &&
9217              !strEqual(chunk_name, chunk_info[i].name))
9218         i++;
9219
9220       if (chunk_info[i].name == NULL)
9221       {
9222         Warn("unknown chunk '%s' in tape file '%s'",
9223               chunk_name, filename);
9224
9225         ReadUnusedBytesFromFile(file, chunk_size);
9226       }
9227       else if (chunk_info[i].size != -1 &&
9228                chunk_info[i].size != chunk_size)
9229       {
9230         Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9231               chunk_size, chunk_name, filename);
9232
9233         ReadUnusedBytesFromFile(file, chunk_size);
9234       }
9235       else
9236       {
9237         // call function to load this tape chunk
9238         int chunk_size_expected =
9239           (chunk_info[i].loader)(file, chunk_size, &tape);
9240
9241         // the size of some chunks cannot be checked before reading other
9242         // chunks first (like "HEAD" and "BODY") that contain some header
9243         // information, so check them here
9244         if (chunk_size_expected != chunk_size)
9245         {
9246           Warn("wrong size (%d) of chunk '%s' in tape file '%s'",
9247                 chunk_size, chunk_name, filename);
9248         }
9249       }
9250     }
9251   }
9252
9253   closeFile(file);
9254
9255   tape.length_frames  = GetTapeLengthFrames();
9256   tape.length_seconds = GetTapeLengthSeconds();
9257
9258 #if 0
9259   Debug("files:LoadTapeFromFilename", "tape file version: %d",
9260         tape.file_version);
9261   Debug("files:LoadTapeFromFilename", "tape game version: %d",
9262         tape.game_version);
9263   Debug("files:LoadTapeFromFilename", "tape engine version: %d",
9264         tape.engine_version);
9265 #endif
9266 }
9267
9268 void LoadTape(int nr)
9269 {
9270   char *filename = getTapeFilename(nr);
9271
9272   LoadTapeFromFilename(filename);
9273 }
9274
9275 void LoadSolutionTape(int nr)
9276 {
9277   char *filename = getSolutionTapeFilename(nr);
9278
9279   LoadTapeFromFilename(filename);
9280
9281   if (TAPE_IS_EMPTY(tape))
9282   {
9283     if (level.game_engine_type == GAME_ENGINE_TYPE_BD &&
9284         level.native_bd_level->replay != NULL)
9285       CopyNativeTape_BD_to_RND(&level);
9286     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP &&
9287         level.native_sp_level->demo.is_available)
9288       CopyNativeTape_SP_to_RND(&level);
9289   }
9290 }
9291
9292 void LoadScoreTape(char *score_tape_basename, int nr)
9293 {
9294   char *filename = getScoreTapeFilename(score_tape_basename, nr);
9295
9296   LoadTapeFromFilename(filename);
9297 }
9298
9299 void LoadScoreCacheTape(char *score_tape_basename, int nr)
9300 {
9301   char *filename = getScoreCacheTapeFilename(score_tape_basename, nr);
9302
9303   LoadTapeFromFilename(filename);
9304 }
9305
9306 static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
9307 {
9308   // chunk required for team mode tapes with non-default screen size
9309   return (tape->num_participating_players > 1 &&
9310           (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
9311            tape->scr_fieldy != SCR_FIELDY_DEFAULT));
9312 }
9313
9314 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
9315 {
9316   putFileVersion(file, tape->file_version);
9317   putFileVersion(file, tape->game_version);
9318 }
9319
9320 static void SaveTape_HEAD(FILE *file, struct TapeInfo *tape)
9321 {
9322   int i;
9323   byte store_participating_players = 0;
9324
9325   // set bits for participating players for compact storage
9326   for (i = 0; i < MAX_PLAYERS; i++)
9327     if (tape->player_participates[i])
9328       store_participating_players |= (1 << i);
9329
9330   putFile32BitBE(file, tape->random_seed);
9331   putFile32BitBE(file, tape->date);
9332   putFile32BitBE(file, tape->length);
9333
9334   putFile8Bit(file, store_participating_players);
9335
9336   putFile8Bit(file, getTapeActionValue(tape));
9337
9338   putFile8Bit(file, tape->property_bits);
9339   putFile8Bit(file, tape->solved);
9340
9341   putFileVersion(file, tape->engine_version);
9342 }
9343
9344 static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
9345 {
9346   putFile8Bit(file, tape->scr_fieldx);
9347   putFile8Bit(file, tape->scr_fieldy);
9348 }
9349
9350 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
9351 {
9352   int level_identifier_size = strlen(tape->level_identifier) + 1;
9353   int i;
9354
9355   putFile16BitBE(file, level_identifier_size);
9356
9357   for (i = 0; i < level_identifier_size; i++)
9358     putFile8Bit(file, tape->level_identifier[i]);
9359
9360   putFile16BitBE(file, tape->level_nr);
9361 }
9362
9363 static void SaveTape_BODY(FILE *file, struct TapeInfo *tape)
9364 {
9365   int i, j;
9366
9367   for (i = 0; i < tape->length; i++)
9368   {
9369     if (tape->use_key_actions)
9370     {
9371       for (j = 0; j < MAX_PLAYERS; j++)
9372         if (tape->player_participates[j])
9373           putFile8Bit(file, tape->pos[i].action[j]);
9374     }
9375
9376     if (tape->use_mouse_actions)
9377     {
9378       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LX]);
9379       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_LY]);
9380       putFile8Bit(file, tape->pos[i].action[TAPE_ACTION_BUTTON]);
9381     }
9382
9383     putFile8Bit(file, tape->pos[i].delay);
9384   }
9385 }
9386
9387 void SaveTapeToFilename(char *filename)
9388 {
9389   FILE *file;
9390   int tape_pos_size;
9391   int info_chunk_size;
9392   int body_chunk_size;
9393
9394   if (!(file = fopen(filename, MODE_WRITE)))
9395   {
9396     Warn("cannot save level recording file '%s'", filename);
9397
9398     return;
9399   }
9400
9401   tape_pos_size = getTapePosSize(&tape);
9402
9403   info_chunk_size = 2 + (strlen(tape.level_identifier) + 1) + 2;
9404   body_chunk_size = tape_pos_size * tape.length;
9405
9406   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
9407   putFileChunkBE(file, "TAPE", CHUNK_SIZE_NONE);
9408
9409   putFileChunkBE(file, "VERS", TAPE_CHUNK_VERS_SIZE);
9410   SaveTape_VERS(file, &tape);
9411
9412   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
9413   SaveTape_HEAD(file, &tape);
9414
9415   if (checkSaveTape_SCRN(&tape))
9416   {
9417     putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
9418     SaveTape_SCRN(file, &tape);
9419   }
9420
9421   putFileChunkBE(file, "INFO", info_chunk_size);
9422   SaveTape_INFO(file, &tape);
9423
9424   putFileChunkBE(file, "BODY", body_chunk_size);
9425   SaveTape_BODY(file, &tape);
9426
9427   fclose(file);
9428
9429   SetFilePermissions(filename, PERMS_PRIVATE);
9430 }
9431
9432 static void SaveTapeExt(char *filename)
9433 {
9434   int i;
9435
9436   tape.file_version = FILE_VERSION_ACTUAL;
9437   tape.game_version = GAME_VERSION_ACTUAL;
9438
9439   tape.num_participating_players = 0;
9440
9441   // count number of participating players
9442   for (i = 0; i < MAX_PLAYERS; i++)
9443     if (tape.player_participates[i])
9444       tape.num_participating_players++;
9445
9446   SaveTapeToFilename(filename);
9447
9448   tape.changed = FALSE;
9449 }
9450
9451 void SaveTape(int nr)
9452 {
9453   char *filename = getTapeFilename(nr);
9454
9455   InitTapeDirectory(leveldir_current->subdir);
9456
9457   SaveTapeExt(filename);
9458 }
9459
9460 void SaveScoreTape(int nr)
9461 {
9462   char *filename = getScoreTapeFilename(tape.score_tape_basename, nr);
9463
9464   // used instead of "leveldir_current->subdir" (for network games)
9465   InitScoreTapeDirectory(levelset.identifier, nr);
9466
9467   SaveTapeExt(filename);
9468 }
9469
9470 static boolean SaveTapeCheckedExt(int nr, char *msg_replace, char *msg_saved,
9471                                   unsigned int req_state_added)
9472 {
9473   char *filename = getTapeFilename(nr);
9474   boolean new_tape = !fileExists(filename);
9475   boolean tape_saved = FALSE;
9476
9477   if (new_tape || Request(msg_replace, REQ_ASK | req_state_added))
9478   {
9479     SaveTape(nr);
9480
9481     if (new_tape)
9482       Request(msg_saved, REQ_CONFIRM | req_state_added);
9483
9484     tape_saved = TRUE;
9485   }
9486
9487   return tape_saved;
9488 }
9489
9490 boolean SaveTapeChecked(int nr)
9491 {
9492   return SaveTapeCheckedExt(nr, "Replace old tape?", "Tape saved!", 0);
9493 }
9494
9495 boolean SaveTapeChecked_LevelSolved(int nr)
9496 {
9497   return SaveTapeCheckedExt(nr, "Level solved! Replace old tape?",
9498                                 "Level solved! Tape saved!", REQ_STAY_OPEN);
9499 }
9500
9501 void DumpTape(struct TapeInfo *tape)
9502 {
9503   int tape_frame_counter;
9504   int i, j;
9505
9506   if (tape->no_valid_file)
9507   {
9508     Warn("cannot dump -- no valid tape file found");
9509
9510     return;
9511   }
9512
9513   PrintLine("-", 79);
9514
9515   Print("Tape of Level %03d (file version %08d, game version %08d)\n",
9516         tape->level_nr, tape->file_version, tape->game_version);
9517   Print("                  (effective engine version %08d)\n",
9518         tape->engine_version);
9519   Print("Level series identifier: '%s'\n", tape->level_identifier);
9520
9521   Print("Solution tape: %s\n",
9522         tape->solved ? "yes" :
9523         tape->game_version < VERSION_IDENT(4,3,2,3) ? "unknown" : "no");
9524
9525   Print("Special tape properties: ");
9526   if (tape->property_bits == TAPE_PROPERTY_NONE)
9527     Print("[none]");
9528   if (tape->property_bits & TAPE_PROPERTY_EM_RANDOM_BUG)
9529     Print("[em_random_bug]");
9530   if (tape->property_bits & TAPE_PROPERTY_GAME_SPEED)
9531     Print("[game_speed]");
9532   if (tape->property_bits & TAPE_PROPERTY_PAUSE_MODE)
9533     Print("[pause]");
9534   if (tape->property_bits & TAPE_PROPERTY_SINGLE_STEP)
9535     Print("[single_step]");
9536   if (tape->property_bits & TAPE_PROPERTY_SNAPSHOT)
9537     Print("[snapshot]");
9538   if (tape->property_bits & TAPE_PROPERTY_REPLAYED)
9539     Print("[replayed]");
9540   if (tape->property_bits & TAPE_PROPERTY_TAS_KEYS)
9541     Print("[tas_keys]");
9542   if (tape->property_bits & TAPE_PROPERTY_SMALL_GRAPHICS)
9543     Print("[small_graphics]");
9544   Print("\n");
9545
9546   int year2 = tape->date / 10000;
9547   int year4 = (year2 < 70 ? 2000 + year2 : 1900 + year2);
9548   int month_index_raw = (tape->date / 100) % 100;
9549   int month_index = month_index_raw % 12;       // prevent invalid index
9550   int month = month_index + 1;
9551   int day = tape->date % 100;
9552
9553   Print("Tape date: %04d-%02d-%02d\n", year4, month, day);
9554
9555   PrintLine("-", 79);
9556
9557   tape_frame_counter = 0;
9558
9559   for (i = 0; i < tape->length; i++)
9560   {
9561     if (i >= MAX_TAPE_LEN)
9562       break;
9563
9564     Print("%04d: ", i);
9565
9566     for (j = 0; j < MAX_PLAYERS; j++)
9567     {
9568       if (tape->player_participates[j])
9569       {
9570         int action = tape->pos[i].action[j];
9571
9572         Print("%d:%02x ", j, action);
9573         Print("[%c%c%c%c|%c%c] - ",
9574               (action & JOY_LEFT ? '<' : ' '),
9575               (action & JOY_RIGHT ? '>' : ' '),
9576               (action & JOY_UP ? '^' : ' '),
9577               (action & JOY_DOWN ? 'v' : ' '),
9578               (action & JOY_BUTTON_1 ? '1' : ' '),
9579               (action & JOY_BUTTON_2 ? '2' : ' '));
9580       }
9581     }
9582
9583     Print("(%03d) ", tape->pos[i].delay);
9584     Print("[%05d]\n", tape_frame_counter);
9585
9586     tape_frame_counter += tape->pos[i].delay;
9587   }
9588
9589   PrintLine("-", 79);
9590 }
9591
9592 void DumpTapes(void)
9593 {
9594   static LevelDirTree *dumptape_leveldir = NULL;
9595
9596   dumptape_leveldir = getTreeInfoFromIdentifier(leveldir_first,
9597                                                 global.dumptape_leveldir);
9598
9599   if (dumptape_leveldir == NULL)
9600     Fail("no such level identifier: '%s'", global.dumptape_leveldir);
9601
9602   if (global.dumptape_level_nr < dumptape_leveldir->first_level ||
9603       global.dumptape_level_nr > dumptape_leveldir->last_level)
9604     Fail("no such level number: %d", global.dumptape_level_nr);
9605
9606   leveldir_current = dumptape_leveldir;
9607
9608   if (options.mytapes)
9609     LoadTape(global.dumptape_level_nr);
9610   else
9611     LoadSolutionTape(global.dumptape_level_nr);
9612
9613   DumpTape(&tape);
9614
9615   CloseAllAndExit(0);
9616 }
9617
9618
9619 // ============================================================================
9620 // score file functions
9621 // ============================================================================
9622
9623 static void setScoreInfoToDefaultsExt(struct ScoreInfo *scores)
9624 {
9625   int i;
9626
9627   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9628   {
9629     strcpy(scores->entry[i].tape_basename, UNDEFINED_FILENAME);
9630     strcpy(scores->entry[i].name, EMPTY_PLAYER_NAME);
9631     scores->entry[i].score = 0;
9632     scores->entry[i].time = 0;
9633
9634     scores->entry[i].id = -1;
9635     strcpy(scores->entry[i].tape_date,    UNKNOWN_NAME);
9636     strcpy(scores->entry[i].platform,     UNKNOWN_NAME);
9637     strcpy(scores->entry[i].version,      UNKNOWN_NAME);
9638     strcpy(scores->entry[i].country_name, UNKNOWN_NAME);
9639     strcpy(scores->entry[i].country_code, "??");
9640   }
9641
9642   scores->num_entries = 0;
9643   scores->last_added = -1;
9644   scores->last_added_local = -1;
9645
9646   scores->updated = FALSE;
9647   scores->uploaded = FALSE;
9648   scores->tape_downloaded = FALSE;
9649   scores->force_last_added = FALSE;
9650
9651   // The following values are intentionally not reset here:
9652   // - last_level_nr
9653   // - last_entry_nr
9654   // - next_level_nr
9655   // - continue_playing
9656   // - continue_on_return
9657 }
9658
9659 static void setScoreInfoToDefaults(void)
9660 {
9661   setScoreInfoToDefaultsExt(&scores);
9662 }
9663
9664 static void setServerScoreInfoToDefaults(void)
9665 {
9666   setScoreInfoToDefaultsExt(&server_scores);
9667 }
9668
9669 static void LoadScore_OLD(int nr)
9670 {
9671   int i;
9672   char *filename = getScoreFilename(nr);
9673   char cookie[MAX_LINE_LEN];
9674   char line[MAX_LINE_LEN];
9675   char *line_ptr;
9676   FILE *file;
9677
9678   if (!(file = fopen(filename, MODE_READ)))
9679     return;
9680
9681   // check file identifier
9682   if (fgets(cookie, MAX_LINE_LEN, file) == NULL)
9683     cookie[0] = '\0';
9684   if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9685     cookie[strlen(cookie) - 1] = '\0';
9686
9687   if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9688   {
9689     Warn("unknown format of score file '%s'", filename);
9690
9691     fclose(file);
9692
9693     return;
9694   }
9695
9696   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9697   {
9698     if (fscanf(file, "%d", &scores.entry[i].score) == EOF)
9699       Warn("fscanf() failed; %s", strerror(errno));
9700
9701     if (fgets(line, MAX_LINE_LEN, file) == NULL)
9702       line[0] = '\0';
9703
9704     if (strlen(line) > 0 && line[strlen(line) - 1] == '\n')
9705       line[strlen(line) - 1] = '\0';
9706
9707     for (line_ptr = line; *line_ptr; line_ptr++)
9708     {
9709       if (*line_ptr != ' ' && *line_ptr != '\t' && *line_ptr != '\0')
9710       {
9711         strncpy(scores.entry[i].name, line_ptr, MAX_PLAYER_NAME_LEN);
9712         scores.entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9713         break;
9714       }
9715     }
9716   }
9717
9718   fclose(file);
9719 }
9720
9721 static void ConvertScore_OLD(void)
9722 {
9723   // only convert score to time for levels that rate playing time over score
9724   if (!level.rate_time_over_score)
9725     return;
9726
9727   // convert old score to playing time for score-less levels (like Supaplex)
9728   int time_final_max = 999;
9729   int i;
9730
9731   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9732   {
9733     int score = scores.entry[i].score;
9734
9735     if (score > 0 && score < time_final_max)
9736       scores.entry[i].time = (time_final_max - score - 1) * FRAMES_PER_SECOND;
9737   }
9738 }
9739
9740 static int LoadScore_VERS(File *file, int chunk_size, struct ScoreInfo *scores)
9741 {
9742   scores->file_version = getFileVersion(file);
9743   scores->game_version = getFileVersion(file);
9744
9745   return chunk_size;
9746 }
9747
9748 static int LoadScore_INFO(File *file, int chunk_size, struct ScoreInfo *scores)
9749 {
9750   char *level_identifier = NULL;
9751   int level_identifier_size;
9752   int i;
9753
9754   level_identifier_size = getFile16BitBE(file);
9755
9756   level_identifier = checked_malloc(level_identifier_size);
9757
9758   for (i = 0; i < level_identifier_size; i++)
9759     level_identifier[i] = getFile8Bit(file);
9760
9761   strncpy(scores->level_identifier, level_identifier, MAX_FILENAME_LEN);
9762   scores->level_identifier[MAX_FILENAME_LEN] = '\0';
9763
9764   checked_free(level_identifier);
9765
9766   scores->level_nr = getFile16BitBE(file);
9767   scores->num_entries = getFile16BitBE(file);
9768
9769   chunk_size = 2 + level_identifier_size + 2 + 2;
9770
9771   return chunk_size;
9772 }
9773
9774 static int LoadScore_NAME(File *file, int chunk_size, struct ScoreInfo *scores)
9775 {
9776   int i, j;
9777
9778   for (i = 0; i < scores->num_entries; i++)
9779   {
9780     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
9781       scores->entry[i].name[j] = getFile8Bit(file);
9782
9783     scores->entry[i].name[MAX_PLAYER_NAME_LEN] = '\0';
9784   }
9785
9786   chunk_size = scores->num_entries * MAX_PLAYER_NAME_LEN;
9787
9788   return chunk_size;
9789 }
9790
9791 static int LoadScore_SCOR(File *file, int chunk_size, struct ScoreInfo *scores)
9792 {
9793   int i;
9794
9795   for (i = 0; i < scores->num_entries; i++)
9796     scores->entry[i].score = getFile16BitBE(file);
9797
9798   chunk_size = scores->num_entries * 2;
9799
9800   return chunk_size;
9801 }
9802
9803 static int LoadScore_SC4R(File *file, int chunk_size, struct ScoreInfo *scores)
9804 {
9805   int i;
9806
9807   for (i = 0; i < scores->num_entries; i++)
9808     scores->entry[i].score = getFile32BitBE(file);
9809
9810   chunk_size = scores->num_entries * 4;
9811
9812   return chunk_size;
9813 }
9814
9815 static int LoadScore_TIME(File *file, int chunk_size, struct ScoreInfo *scores)
9816 {
9817   int i;
9818
9819   for (i = 0; i < scores->num_entries; i++)
9820     scores->entry[i].time = getFile32BitBE(file);
9821
9822   chunk_size = scores->num_entries * 4;
9823
9824   return chunk_size;
9825 }
9826
9827 static int LoadScore_TAPE(File *file, int chunk_size, struct ScoreInfo *scores)
9828 {
9829   int i, j;
9830
9831   for (i = 0; i < scores->num_entries; i++)
9832   {
9833     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
9834       scores->entry[i].tape_basename[j] = getFile8Bit(file);
9835
9836     scores->entry[i].tape_basename[MAX_SCORE_TAPE_BASENAME_LEN] = '\0';
9837   }
9838
9839   chunk_size = scores->num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
9840
9841   return chunk_size;
9842 }
9843
9844 void LoadScore(int nr)
9845 {
9846   char *filename = getScoreFilename(nr);
9847   char cookie[MAX_LINE_LEN];
9848   char chunk_name[CHUNK_ID_LEN + 1];
9849   int chunk_size;
9850   boolean old_score_file_format = FALSE;
9851   File *file;
9852
9853   // always start with reliable default values
9854   setScoreInfoToDefaults();
9855
9856   if (!(file = openFile(filename, MODE_READ)))
9857     return;
9858
9859   getFileChunkBE(file, chunk_name, NULL);
9860   if (strEqual(chunk_name, "RND1"))
9861   {
9862     getFile32BitBE(file);               // not used
9863
9864     getFileChunkBE(file, chunk_name, NULL);
9865     if (!strEqual(chunk_name, "SCOR"))
9866     {
9867       Warn("unknown format of score file '%s'", filename);
9868
9869       closeFile(file);
9870
9871       return;
9872     }
9873   }
9874   else  // check for old file format with cookie string
9875   {
9876     strcpy(cookie, chunk_name);
9877     if (getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4) == NULL)
9878       cookie[4] = '\0';
9879     if (strlen(cookie) > 0 && cookie[strlen(cookie) - 1] == '\n')
9880       cookie[strlen(cookie) - 1] = '\0';
9881
9882     if (!checkCookieString(cookie, SCORE_COOKIE_TMPL))
9883     {
9884       Warn("unknown format of score file '%s'", filename);
9885
9886       closeFile(file);
9887
9888       return;
9889     }
9890
9891     old_score_file_format = TRUE;
9892   }
9893
9894   if (old_score_file_format)
9895   {
9896     // score files from versions before 4.2.4.0 without chunk structure
9897     LoadScore_OLD(nr);
9898
9899     // convert score to time, if possible (mainly for Supaplex levels)
9900     ConvertScore_OLD();
9901   }
9902   else
9903   {
9904     static struct
9905     {
9906       char *name;
9907       int size;
9908       int (*loader)(File *, int, struct ScoreInfo *);
9909     }
9910     chunk_info[] =
9911     {
9912       { "VERS", SCORE_CHUNK_VERS_SIZE,  LoadScore_VERS },
9913       { "INFO", -1,                     LoadScore_INFO },
9914       { "NAME", -1,                     LoadScore_NAME },
9915       { "SCOR", -1,                     LoadScore_SCOR },
9916       { "SC4R", -1,                     LoadScore_SC4R },
9917       { "TIME", -1,                     LoadScore_TIME },
9918       { "TAPE", -1,                     LoadScore_TAPE },
9919
9920       {  NULL,  0,                      NULL }
9921     };
9922
9923     while (getFileChunkBE(file, chunk_name, &chunk_size))
9924     {
9925       int i = 0;
9926
9927       while (chunk_info[i].name != NULL &&
9928              !strEqual(chunk_name, chunk_info[i].name))
9929         i++;
9930
9931       if (chunk_info[i].name == NULL)
9932       {
9933         Warn("unknown chunk '%s' in score file '%s'",
9934               chunk_name, filename);
9935
9936         ReadUnusedBytesFromFile(file, chunk_size);
9937       }
9938       else if (chunk_info[i].size != -1 &&
9939                chunk_info[i].size != chunk_size)
9940       {
9941         Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9942               chunk_size, chunk_name, filename);
9943
9944         ReadUnusedBytesFromFile(file, chunk_size);
9945       }
9946       else
9947       {
9948         // call function to load this score chunk
9949         int chunk_size_expected =
9950           (chunk_info[i].loader)(file, chunk_size, &scores);
9951
9952         // the size of some chunks cannot be checked before reading other
9953         // chunks first (like "HEAD" and "BODY") that contain some header
9954         // information, so check them here
9955         if (chunk_size_expected != chunk_size)
9956         {
9957           Warn("wrong size (%d) of chunk '%s' in score file '%s'",
9958                 chunk_size, chunk_name, filename);
9959         }
9960       }
9961     }
9962   }
9963
9964   closeFile(file);
9965 }
9966
9967 #if ENABLE_HISTORIC_CHUNKS
9968 void SaveScore_OLD(int nr)
9969 {
9970   int i;
9971   char *filename = getScoreFilename(nr);
9972   FILE *file;
9973
9974   // used instead of "leveldir_current->subdir" (for network games)
9975   InitScoreDirectory(levelset.identifier);
9976
9977   if (!(file = fopen(filename, MODE_WRITE)))
9978   {
9979     Warn("cannot save score for level %d", nr);
9980
9981     return;
9982   }
9983
9984   fprintf(file, "%s\n\n", SCORE_COOKIE);
9985
9986   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
9987     fprintf(file, "%d %s\n", scores.entry[i].score, scores.entry[i].name);
9988
9989   fclose(file);
9990
9991   SetFilePermissions(filename, PERMS_PRIVATE);
9992 }
9993 #endif
9994
9995 static void SaveScore_VERS(FILE *file, struct ScoreInfo *scores)
9996 {
9997   putFileVersion(file, scores->file_version);
9998   putFileVersion(file, scores->game_version);
9999 }
10000
10001 static void SaveScore_INFO(FILE *file, struct ScoreInfo *scores)
10002 {
10003   int level_identifier_size = strlen(scores->level_identifier) + 1;
10004   int i;
10005
10006   putFile16BitBE(file, level_identifier_size);
10007
10008   for (i = 0; i < level_identifier_size; i++)
10009     putFile8Bit(file, scores->level_identifier[i]);
10010
10011   putFile16BitBE(file, scores->level_nr);
10012   putFile16BitBE(file, scores->num_entries);
10013 }
10014
10015 static void SaveScore_NAME(FILE *file, struct ScoreInfo *scores)
10016 {
10017   int i, j;
10018
10019   for (i = 0; i < scores->num_entries; i++)
10020   {
10021     int name_size = strlen(scores->entry[i].name);
10022
10023     for (j = 0; j < MAX_PLAYER_NAME_LEN; j++)
10024       putFile8Bit(file, (j < name_size ? scores->entry[i].name[j] : 0));
10025   }
10026 }
10027
10028 static void SaveScore_SCOR(FILE *file, struct ScoreInfo *scores)
10029 {
10030   int i;
10031
10032   for (i = 0; i < scores->num_entries; i++)
10033     putFile16BitBE(file, scores->entry[i].score);
10034 }
10035
10036 static void SaveScore_SC4R(FILE *file, struct ScoreInfo *scores)
10037 {
10038   int i;
10039
10040   for (i = 0; i < scores->num_entries; i++)
10041     putFile32BitBE(file, scores->entry[i].score);
10042 }
10043
10044 static void SaveScore_TIME(FILE *file, struct ScoreInfo *scores)
10045 {
10046   int i;
10047
10048   for (i = 0; i < scores->num_entries; i++)
10049     putFile32BitBE(file, scores->entry[i].time);
10050 }
10051
10052 static void SaveScore_TAPE(FILE *file, struct ScoreInfo *scores)
10053 {
10054   int i, j;
10055
10056   for (i = 0; i < scores->num_entries; i++)
10057   {
10058     int size = strlen(scores->entry[i].tape_basename);
10059
10060     for (j = 0; j < MAX_SCORE_TAPE_BASENAME_LEN; j++)
10061       putFile8Bit(file, (j < size ? scores->entry[i].tape_basename[j] : 0));
10062   }
10063 }
10064
10065 static void SaveScoreToFilename(char *filename)
10066 {
10067   FILE *file;
10068   int info_chunk_size;
10069   int name_chunk_size;
10070   int scor_chunk_size;
10071   int sc4r_chunk_size;
10072   int time_chunk_size;
10073   int tape_chunk_size;
10074   boolean has_large_score_values;
10075   int i;
10076
10077   if (!(file = fopen(filename, MODE_WRITE)))
10078   {
10079     Warn("cannot save score file '%s'", filename);
10080
10081     return;
10082   }
10083
10084   info_chunk_size = 2 + (strlen(scores.level_identifier) + 1) + 2 + 2;
10085   name_chunk_size = scores.num_entries * MAX_PLAYER_NAME_LEN;
10086   scor_chunk_size = scores.num_entries * 2;
10087   sc4r_chunk_size = scores.num_entries * 4;
10088   time_chunk_size = scores.num_entries * 4;
10089   tape_chunk_size = scores.num_entries * MAX_SCORE_TAPE_BASENAME_LEN;
10090
10091   has_large_score_values = FALSE;
10092   for (i = 0; i < scores.num_entries; i++)
10093     if (scores.entry[i].score > 0xffff)
10094       has_large_score_values = TRUE;
10095
10096   putFileChunkBE(file, "RND1", CHUNK_SIZE_UNDEFINED);
10097   putFileChunkBE(file, "SCOR", CHUNK_SIZE_NONE);
10098
10099   putFileChunkBE(file, "VERS", SCORE_CHUNK_VERS_SIZE);
10100   SaveScore_VERS(file, &scores);
10101
10102   putFileChunkBE(file, "INFO", info_chunk_size);
10103   SaveScore_INFO(file, &scores);
10104
10105   putFileChunkBE(file, "NAME", name_chunk_size);
10106   SaveScore_NAME(file, &scores);
10107
10108   if (has_large_score_values)
10109   {
10110     putFileChunkBE(file, "SC4R", sc4r_chunk_size);
10111     SaveScore_SC4R(file, &scores);
10112   }
10113   else
10114   {
10115     putFileChunkBE(file, "SCOR", scor_chunk_size);
10116     SaveScore_SCOR(file, &scores);
10117   }
10118
10119   putFileChunkBE(file, "TIME", time_chunk_size);
10120   SaveScore_TIME(file, &scores);
10121
10122   putFileChunkBE(file, "TAPE", tape_chunk_size);
10123   SaveScore_TAPE(file, &scores);
10124
10125   fclose(file);
10126
10127   SetFilePermissions(filename, PERMS_PRIVATE);
10128 }
10129
10130 void SaveScore(int nr)
10131 {
10132   char *filename = getScoreFilename(nr);
10133   int i;
10134
10135   // used instead of "leveldir_current->subdir" (for network games)
10136   InitScoreDirectory(levelset.identifier);
10137
10138   scores.file_version = FILE_VERSION_ACTUAL;
10139   scores.game_version = GAME_VERSION_ACTUAL;
10140
10141   strncpy(scores.level_identifier, levelset.identifier, MAX_FILENAME_LEN);
10142   scores.level_identifier[MAX_FILENAME_LEN] = '\0';
10143   scores.level_nr = level_nr;
10144
10145   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10146     if (scores.entry[i].score == 0 &&
10147         scores.entry[i].time == 0 &&
10148         strEqual(scores.entry[i].name, EMPTY_PLAYER_NAME))
10149       break;
10150
10151   scores.num_entries = i;
10152
10153   if (scores.num_entries == 0)
10154     return;
10155
10156   SaveScoreToFilename(filename);
10157 }
10158
10159 static void LoadServerScoreFromCache(int nr)
10160 {
10161   struct ScoreEntry score_entry;
10162   struct
10163   {
10164     void *value;
10165     boolean is_string;
10166     int string_size;
10167   }
10168   score_mapping[] =
10169   {
10170     { &score_entry.score,               FALSE,  0                       },
10171     { &score_entry.time,                FALSE,  0                       },
10172     { score_entry.name,                 TRUE,   MAX_PLAYER_NAME_LEN     },
10173     { score_entry.tape_basename,        TRUE,   MAX_FILENAME_LEN        },
10174     { score_entry.tape_date,            TRUE,   MAX_ISO_DATE_LEN        },
10175     { &score_entry.id,                  FALSE,  0                       },
10176     { score_entry.platform,             TRUE,   MAX_PLATFORM_TEXT_LEN   },
10177     { score_entry.version,              TRUE,   MAX_VERSION_TEXT_LEN    },
10178     { score_entry.country_code,         TRUE,   MAX_COUNTRY_CODE_LEN    },
10179     { score_entry.country_name,         TRUE,   MAX_COUNTRY_NAME_LEN    },
10180
10181     { NULL,                             FALSE,  0                       }
10182   };
10183   char *filename = getScoreCacheFilename(nr);
10184   SetupFileHash *score_hash = loadSetupFileHash(filename);
10185   int i, j;
10186
10187   server_scores.num_entries = 0;
10188
10189   if (score_hash == NULL)
10190     return;
10191
10192   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
10193   {
10194     score_entry = server_scores.entry[i];
10195
10196     for (j = 0; score_mapping[j].value != NULL; j++)
10197     {
10198       char token[10];
10199
10200       sprintf(token, "%02d.%d", i, j);
10201
10202       char *value = getHashEntry(score_hash, token);
10203
10204       if (value == NULL)
10205         continue;
10206
10207       if (score_mapping[j].is_string)
10208       {
10209         char *score_value = (char *)score_mapping[j].value;
10210         int value_size = score_mapping[j].string_size;
10211
10212         strncpy(score_value, value, value_size);
10213         score_value[value_size] = '\0';
10214       }
10215       else
10216       {
10217         int *score_value = (int *)score_mapping[j].value;
10218
10219         *score_value = atoi(value);
10220       }
10221
10222       server_scores.num_entries = i + 1;
10223     }
10224
10225     server_scores.entry[i] = score_entry;
10226   }
10227
10228   freeSetupFileHash(score_hash);
10229 }
10230
10231 void LoadServerScore(int nr, boolean download_score)
10232 {
10233   if (!setup.use_api_server)
10234     return;
10235
10236   // always start with reliable default values
10237   setServerScoreInfoToDefaults();
10238
10239   // 1st step: load server scores from cache file (which may not exist)
10240   // (this should prevent reading it while the thread is writing to it)
10241   LoadServerScoreFromCache(nr);
10242
10243   if (download_score && runtime.use_api_server)
10244   {
10245     // 2nd step: download server scores from score server to cache file
10246     // (as thread, as it might time out if the server is not reachable)
10247     ApiGetScoreAsThread(nr);
10248   }
10249 }
10250
10251 void PrepareScoreTapesForUpload(char *leveldir_subdir)
10252 {
10253   MarkTapeDirectoryUploadsAsIncomplete(leveldir_subdir);
10254
10255   // if score tape not uploaded, ask for uploading missing tapes later
10256   if (!setup.has_remaining_tapes)
10257     setup.ask_for_remaining_tapes = TRUE;
10258
10259   setup.provide_uploading_tapes = TRUE;
10260   setup.has_remaining_tapes = TRUE;
10261
10262   SaveSetup_ServerSetup();
10263 }
10264
10265 void SaveServerScore(int nr, boolean tape_saved)
10266 {
10267   if (!runtime.use_api_server)
10268   {
10269     PrepareScoreTapesForUpload(leveldir_current->subdir);
10270
10271     return;
10272   }
10273
10274   ApiAddScoreAsThread(nr, tape_saved, NULL);
10275 }
10276
10277 void SaveServerScoreFromFile(int nr, boolean tape_saved,
10278                              char *score_tape_filename)
10279 {
10280   if (!runtime.use_api_server)
10281     return;
10282
10283   ApiAddScoreAsThread(nr, tape_saved, score_tape_filename);
10284 }
10285
10286 void LoadLocalAndServerScore(int nr, boolean download_score)
10287 {
10288   int last_added_local = scores.last_added_local;
10289   boolean force_last_added = scores.force_last_added;
10290
10291   // needed if only showing server scores
10292   setScoreInfoToDefaults();
10293
10294   if (!strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_SERVER_ONLY))
10295     LoadScore(nr);
10296
10297   // restore last added local score entry (before merging server scores)
10298   scores.last_added = scores.last_added_local = last_added_local;
10299
10300   if (setup.use_api_server &&
10301       !strEqual(setup.scores_in_highscore_list, STR_SCORES_TYPE_LOCAL_ONLY))
10302   {
10303     // load server scores from cache file and trigger update from server
10304     LoadServerScore(nr, download_score);
10305
10306     // merge local scores with scores from server
10307     MergeServerScore();
10308   }
10309
10310   if (force_last_added)
10311     scores.force_last_added = force_last_added;
10312 }
10313
10314
10315 // ============================================================================
10316 // setup file functions
10317 // ============================================================================
10318
10319 #define TOKEN_STR_PLAYER_PREFIX                 "player_"
10320
10321
10322 static struct TokenInfo global_setup_tokens[] =
10323 {
10324   {
10325     TYPE_STRING,
10326     &setup.player_name,                         "player_name"
10327   },
10328   {
10329     TYPE_SWITCH,
10330     &setup.multiple_users,                      "multiple_users"
10331   },
10332   {
10333     TYPE_SWITCH,
10334     &setup.sound,                               "sound"
10335   },
10336   {
10337     TYPE_SWITCH,
10338     &setup.sound_loops,                         "repeating_sound_loops"
10339   },
10340   {
10341     TYPE_SWITCH,
10342     &setup.sound_music,                         "background_music"
10343   },
10344   {
10345     TYPE_SWITCH,
10346     &setup.sound_simple,                        "simple_sound_effects"
10347   },
10348   {
10349     TYPE_SWITCH,
10350     &setup.toons,                               "toons"
10351   },
10352   {
10353     TYPE_SWITCH,
10354     &setup.global_animations,                   "global_animations"
10355   },
10356   {
10357     TYPE_SWITCH,
10358     &setup.scroll_delay,                        "scroll_delay"
10359   },
10360   {
10361     TYPE_SWITCH,
10362     &setup.forced_scroll_delay,                 "forced_scroll_delay"
10363   },
10364   {
10365     TYPE_INTEGER,
10366     &setup.scroll_delay_value,                  "scroll_delay_value"
10367   },
10368   {
10369     TYPE_STRING,
10370     &setup.engine_snapshot_mode,                "engine_snapshot_mode"
10371   },
10372   {
10373     TYPE_INTEGER,
10374     &setup.engine_snapshot_memory,              "engine_snapshot_memory"
10375   },
10376   {
10377     TYPE_SWITCH,
10378     &setup.fade_screens,                        "fade_screens"
10379   },
10380   {
10381     TYPE_SWITCH,
10382     &setup.autorecord,                          "automatic_tape_recording"
10383   },
10384   {
10385     TYPE_SWITCH,
10386     &setup.autorecord_after_replay,             "autorecord_after_replay"
10387   },
10388   {
10389     TYPE_SWITCH,
10390     &setup.auto_pause_on_start,                 "auto_pause_on_start"
10391   },
10392   {
10393     TYPE_SWITCH,
10394     &setup.show_titlescreen,                    "show_titlescreen"
10395   },
10396   {
10397     TYPE_SWITCH,
10398     &setup.quick_doors,                         "quick_doors"
10399   },
10400   {
10401     TYPE_SWITCH,
10402     &setup.team_mode,                           "team_mode"
10403   },
10404   {
10405     TYPE_SWITCH,
10406     &setup.handicap,                            "handicap"
10407   },
10408   {
10409     TYPE_SWITCH,
10410     &setup.skip_levels,                         "skip_levels"
10411   },
10412   {
10413     TYPE_SWITCH,
10414     &setup.increment_levels,                    "increment_levels"
10415   },
10416   {
10417     TYPE_SWITCH,
10418     &setup.auto_play_next_level,                "auto_play_next_level"
10419   },
10420   {
10421     TYPE_SWITCH,
10422     &setup.count_score_after_game,              "count_score_after_game"
10423   },
10424   {
10425     TYPE_SWITCH,
10426     &setup.show_scores_after_game,              "show_scores_after_game"
10427   },
10428   {
10429     TYPE_SWITCH,
10430     &setup.time_limit,                          "time_limit"
10431   },
10432   {
10433     TYPE_SWITCH,
10434     &setup.fullscreen,                          "fullscreen"
10435   },
10436   {
10437     TYPE_INTEGER,
10438     &setup.window_scaling_percent,              "window_scaling_percent"
10439   },
10440   {
10441     TYPE_STRING,
10442     &setup.window_scaling_quality,              "window_scaling_quality"
10443   },
10444   {
10445     TYPE_STRING,
10446     &setup.screen_rendering_mode,               "screen_rendering_mode"
10447   },
10448   {
10449     TYPE_STRING,
10450     &setup.vsync_mode,                          "vsync_mode"
10451   },
10452   {
10453     TYPE_SWITCH,
10454     &setup.ask_on_escape,                       "ask_on_escape"
10455   },
10456   {
10457     TYPE_SWITCH,
10458     &setup.ask_on_escape_editor,                "ask_on_escape_editor"
10459   },
10460   {
10461     TYPE_SWITCH,
10462     &setup.ask_on_game_over,                    "ask_on_game_over"
10463   },
10464   {
10465     TYPE_SWITCH,
10466     &setup.ask_on_quit_game,                    "ask_on_quit_game"
10467   },
10468   {
10469     TYPE_SWITCH,
10470     &setup.ask_on_quit_program,                 "ask_on_quit_program"
10471   },
10472   {
10473     TYPE_SWITCH,
10474     &setup.quick_switch,                        "quick_player_switch"
10475   },
10476   {
10477     TYPE_SWITCH,
10478     &setup.input_on_focus,                      "input_on_focus"
10479   },
10480   {
10481     TYPE_SWITCH,
10482     &setup.prefer_aga_graphics,                 "prefer_aga_graphics"
10483   },
10484   {
10485     TYPE_SWITCH,
10486     &setup.prefer_lowpass_sounds,               "prefer_lowpass_sounds"
10487   },
10488   {
10489     TYPE_SWITCH,
10490     &setup.prefer_extra_panel_items,            "prefer_extra_panel_items"
10491   },
10492   {
10493     TYPE_SWITCH,
10494     &setup.game_speed_extended,                 "game_speed_extended"
10495   },
10496   {
10497     TYPE_INTEGER,
10498     &setup.game_frame_delay,                    "game_frame_delay"
10499   },
10500   {
10501     TYPE_SWITCH,
10502     &setup.bd_skip_uncovering,                  "bd_skip_uncovering"
10503   },
10504   {
10505     TYPE_SWITCH,
10506     &setup.bd_skip_hatching,                    "bd_skip_hatching"
10507   },
10508   {
10509     TYPE_SWITCH,
10510     &setup.bd_scroll_delay,                     "bd_scroll_delay"
10511   },
10512   {
10513     TYPE_SWITCH3,
10514     &setup.bd_smooth_movements,                 "bd_smooth_movements"
10515   },
10516   {
10517     TYPE_SWITCH,
10518     &setup.sp_show_border_elements,             "sp_show_border_elements"
10519   },
10520   {
10521     TYPE_SWITCH,
10522     &setup.small_game_graphics,                 "small_game_graphics"
10523   },
10524   {
10525     TYPE_SWITCH,
10526     &setup.show_load_save_buttons,              "show_load_save_buttons"
10527   },
10528   {
10529     TYPE_SWITCH,
10530     &setup.show_undo_redo_buttons,              "show_undo_redo_buttons"
10531   },
10532   {
10533     TYPE_STRING,
10534     &setup.scores_in_highscore_list,            "scores_in_highscore_list"
10535   },
10536   {
10537     TYPE_STRING,
10538     &setup.graphics_set,                        "graphics_set"
10539   },
10540   {
10541     TYPE_STRING,
10542     &setup.sounds_set,                          "sounds_set"
10543   },
10544   {
10545     TYPE_STRING,
10546     &setup.music_set,                           "music_set"
10547   },
10548   {
10549     TYPE_SWITCH3,
10550     &setup.override_level_graphics,             "override_level_graphics"
10551   },
10552   {
10553     TYPE_SWITCH3,
10554     &setup.override_level_sounds,               "override_level_sounds"
10555   },
10556   {
10557     TYPE_SWITCH3,
10558     &setup.override_level_music,                "override_level_music"
10559   },
10560   {
10561     TYPE_INTEGER,
10562     &setup.volume_simple,                       "volume_simple"
10563   },
10564   {
10565     TYPE_INTEGER,
10566     &setup.volume_loops,                        "volume_loops"
10567   },
10568   {
10569     TYPE_INTEGER,
10570     &setup.volume_music,                        "volume_music"
10571   },
10572   {
10573     TYPE_SWITCH,
10574     &setup.network_mode,                        "network_mode"
10575   },
10576   {
10577     TYPE_PLAYER,
10578     &setup.network_player_nr,                   "network_player"
10579   },
10580   {
10581     TYPE_STRING,
10582     &setup.network_server_hostname,             "network_server_hostname"
10583   },
10584   {
10585     TYPE_STRING,
10586     &setup.touch.control_type,                  "touch.control_type"
10587   },
10588   {
10589     TYPE_INTEGER,
10590     &setup.touch.move_distance,                 "touch.move_distance"
10591   },
10592   {
10593     TYPE_INTEGER,
10594     &setup.touch.drop_distance,                 "touch.drop_distance"
10595   },
10596   {
10597     TYPE_INTEGER,
10598     &setup.touch.transparency,                  "touch.transparency"
10599   },
10600   {
10601     TYPE_INTEGER,
10602     &setup.touch.draw_outlined,                 "touch.draw_outlined"
10603   },
10604   {
10605     TYPE_INTEGER,
10606     &setup.touch.draw_pressed,                  "touch.draw_pressed"
10607   },
10608   {
10609     TYPE_INTEGER,
10610     &setup.touch.grid_xsize[0],                 "touch.virtual_buttons.0.xsize"
10611   },
10612   {
10613     TYPE_INTEGER,
10614     &setup.touch.grid_ysize[0],                 "touch.virtual_buttons.0.ysize"
10615   },
10616   {
10617     TYPE_INTEGER,
10618     &setup.touch.grid_xsize[1],                 "touch.virtual_buttons.1.xsize"
10619   },
10620   {
10621     TYPE_INTEGER,
10622     &setup.touch.grid_ysize[1],                 "touch.virtual_buttons.1.ysize"
10623   },
10624   {
10625     TYPE_SWITCH,
10626     &setup.touch.overlay_buttons,               "touch.overlay_buttons"
10627   },
10628 };
10629
10630 static struct TokenInfo auto_setup_tokens[] =
10631 {
10632   {
10633     TYPE_INTEGER,
10634     &setup.auto_setup.editor_zoom_tilesize,     "editor.zoom_tilesize"
10635   },
10636 };
10637
10638 static struct TokenInfo server_setup_tokens[] =
10639 {
10640   {
10641     TYPE_STRING,
10642     &setup.player_uuid,                         "player_uuid"
10643   },
10644   {
10645     TYPE_INTEGER,
10646     &setup.player_version,                      "player_version"
10647   },
10648   {
10649     TYPE_SWITCH,
10650     &setup.use_api_server,          TEST_PREFIX "use_api_server"
10651   },
10652   {
10653     TYPE_STRING,
10654     &setup.api_server_hostname,     TEST_PREFIX "api_server_hostname"
10655   },
10656   {
10657     TYPE_STRING,
10658     &setup.api_server_password,     TEST_PREFIX "api_server_password"
10659   },
10660   {
10661     TYPE_SWITCH,
10662     &setup.ask_for_uploading_tapes, TEST_PREFIX "ask_for_uploading_tapes"
10663   },
10664   {
10665     TYPE_SWITCH,
10666     &setup.ask_for_remaining_tapes, TEST_PREFIX "ask_for_remaining_tapes"
10667   },
10668   {
10669     TYPE_SWITCH,
10670     &setup.provide_uploading_tapes, TEST_PREFIX "provide_uploading_tapes"
10671   },
10672   {
10673     TYPE_SWITCH,
10674     &setup.ask_for_using_api_server,TEST_PREFIX "ask_for_using_api_server"
10675   },
10676   {
10677     TYPE_SWITCH,
10678     &setup.has_remaining_tapes,     TEST_PREFIX "has_remaining_tapes"
10679   },
10680 };
10681
10682 static struct TokenInfo editor_setup_tokens[] =
10683 {
10684   {
10685     TYPE_SWITCH,
10686     &setup.editor.el_classic,                   "editor.el_classic"
10687   },
10688   {
10689     TYPE_SWITCH,
10690     &setup.editor.el_custom,                    "editor.el_custom"
10691   },
10692   {
10693     TYPE_SWITCH,
10694     &setup.editor.el_user_defined,              "editor.el_user_defined"
10695   },
10696   {
10697     TYPE_SWITCH,
10698     &setup.editor.el_dynamic,                   "editor.el_dynamic"
10699   },
10700   {
10701     TYPE_SWITCH,
10702     &setup.editor.el_headlines,                 "editor.el_headlines"
10703   },
10704   {
10705     TYPE_SWITCH,
10706     &setup.editor.show_element_token,           "editor.show_element_token"
10707   },
10708   {
10709     TYPE_SWITCH,
10710     &setup.editor.show_read_only_warning,       "editor.show_read_only_warning"
10711   },
10712 };
10713
10714 static struct TokenInfo editor_cascade_setup_tokens[] =
10715 {
10716   {
10717     TYPE_SWITCH,
10718     &setup.editor_cascade.el_bd,                "editor.cascade.el_bd"
10719   },
10720   {
10721     TYPE_SWITCH,
10722     &setup.editor_cascade.el_bd_native,         "editor.cascade.el_bd_native"
10723   },
10724   {
10725     TYPE_SWITCH,
10726     &setup.editor_cascade.el_bd_effects,        "editor.cascade.el_bd_effects"
10727   },
10728   {
10729     TYPE_SWITCH,
10730     &setup.editor_cascade.el_em,                "editor.cascade.el_em"
10731   },
10732   {
10733     TYPE_SWITCH,
10734     &setup.editor_cascade.el_emc,               "editor.cascade.el_emc"
10735   },
10736   {
10737     TYPE_SWITCH,
10738     &setup.editor_cascade.el_rnd,               "editor.cascade.el_rnd"
10739   },
10740   {
10741     TYPE_SWITCH,
10742     &setup.editor_cascade.el_sb,                "editor.cascade.el_sb"
10743   },
10744   {
10745     TYPE_SWITCH,
10746     &setup.editor_cascade.el_sp,                "editor.cascade.el_sp"
10747   },
10748   {
10749     TYPE_SWITCH,
10750     &setup.editor_cascade.el_dc,                "editor.cascade.el_dc"
10751   },
10752   {
10753     TYPE_SWITCH,
10754     &setup.editor_cascade.el_dx,                "editor.cascade.el_dx"
10755   },
10756   {
10757     TYPE_SWITCH,
10758     &setup.editor_cascade.el_mm,                "editor.cascade.el_mm"
10759   },
10760   {
10761     TYPE_SWITCH,
10762     &setup.editor_cascade.el_df,                "editor.cascade.el_df"
10763   },
10764   {
10765     TYPE_SWITCH,
10766     &setup.editor_cascade.el_chars,             "editor.cascade.el_chars"
10767   },
10768   {
10769     TYPE_SWITCH,
10770     &setup.editor_cascade.el_steel_chars,       "editor.cascade.el_steel_chars"
10771   },
10772   {
10773     TYPE_SWITCH,
10774     &setup.editor_cascade.el_ce,                "editor.cascade.el_ce"
10775   },
10776   {
10777     TYPE_SWITCH,
10778     &setup.editor_cascade.el_ge,                "editor.cascade.el_ge"
10779   },
10780   {
10781     TYPE_SWITCH,
10782     &setup.editor_cascade.el_es,                "editor.cascade.el_es"
10783   },
10784   {
10785     TYPE_SWITCH,
10786     &setup.editor_cascade.el_ref,               "editor.cascade.el_ref"
10787   },
10788   {
10789     TYPE_SWITCH,
10790     &setup.editor_cascade.el_user,              "editor.cascade.el_user"
10791   },
10792   {
10793     TYPE_SWITCH,
10794     &setup.editor_cascade.el_dynamic,           "editor.cascade.el_dynamic"
10795   },
10796 };
10797
10798 static struct TokenInfo shortcut_setup_tokens[] =
10799 {
10800   {
10801     TYPE_KEY_X11,
10802     &setup.shortcut.save_game,                  "shortcut.save_game"
10803   },
10804   {
10805     TYPE_KEY_X11,
10806     &setup.shortcut.load_game,                  "shortcut.load_game"
10807   },
10808   {
10809     TYPE_KEY_X11,
10810     &setup.shortcut.restart_game,               "shortcut.restart_game"
10811   },
10812   {
10813     TYPE_KEY_X11,
10814     &setup.shortcut.pause_before_end,           "shortcut.pause_before_end"
10815   },
10816   {
10817     TYPE_KEY_X11,
10818     &setup.shortcut.toggle_pause,               "shortcut.toggle_pause"
10819   },
10820   {
10821     TYPE_KEY_X11,
10822     &setup.shortcut.focus_player[0],            "shortcut.focus_player_1"
10823   },
10824   {
10825     TYPE_KEY_X11,
10826     &setup.shortcut.focus_player[1],            "shortcut.focus_player_2"
10827   },
10828   {
10829     TYPE_KEY_X11,
10830     &setup.shortcut.focus_player[2],            "shortcut.focus_player_3"
10831   },
10832   {
10833     TYPE_KEY_X11,
10834     &setup.shortcut.focus_player[3],            "shortcut.focus_player_4"
10835   },
10836   {
10837     TYPE_KEY_X11,
10838     &setup.shortcut.focus_player_all,           "shortcut.focus_player_all"
10839   },
10840   {
10841     TYPE_KEY_X11,
10842     &setup.shortcut.tape_eject,                 "shortcut.tape_eject"
10843   },
10844   {
10845     TYPE_KEY_X11,
10846     &setup.shortcut.tape_extra,                 "shortcut.tape_extra"
10847   },
10848   {
10849     TYPE_KEY_X11,
10850     &setup.shortcut.tape_stop,                  "shortcut.tape_stop"
10851   },
10852   {
10853     TYPE_KEY_X11,
10854     &setup.shortcut.tape_pause,                 "shortcut.tape_pause"
10855   },
10856   {
10857     TYPE_KEY_X11,
10858     &setup.shortcut.tape_record,                "shortcut.tape_record"
10859   },
10860   {
10861     TYPE_KEY_X11,
10862     &setup.shortcut.tape_play,                  "shortcut.tape_play"
10863   },
10864   {
10865     TYPE_KEY_X11,
10866     &setup.shortcut.sound_simple,               "shortcut.sound_simple"
10867   },
10868   {
10869     TYPE_KEY_X11,
10870     &setup.shortcut.sound_loops,                "shortcut.sound_loops"
10871   },
10872   {
10873     TYPE_KEY_X11,
10874     &setup.shortcut.sound_music,                "shortcut.sound_music"
10875   },
10876   {
10877     TYPE_KEY_X11,
10878     &setup.shortcut.snap_left,                  "shortcut.snap_left"
10879   },
10880   {
10881     TYPE_KEY_X11,
10882     &setup.shortcut.snap_right,                 "shortcut.snap_right"
10883   },
10884   {
10885     TYPE_KEY_X11,
10886     &setup.shortcut.snap_up,                    "shortcut.snap_up"
10887   },
10888   {
10889     TYPE_KEY_X11,
10890     &setup.shortcut.snap_down,                  "shortcut.snap_down"
10891   },
10892 };
10893
10894 static struct SetupInputInfo setup_input;
10895 static struct TokenInfo player_setup_tokens[] =
10896 {
10897   {
10898     TYPE_BOOLEAN,
10899     &setup_input.use_joystick,                  ".use_joystick"
10900   },
10901   {
10902     TYPE_STRING,
10903     &setup_input.joy.device_name,               ".joy.device_name"
10904   },
10905   {
10906     TYPE_INTEGER,
10907     &setup_input.joy.xleft,                     ".joy.xleft"
10908   },
10909   {
10910     TYPE_INTEGER,
10911     &setup_input.joy.xmiddle,                   ".joy.xmiddle"
10912   },
10913   {
10914     TYPE_INTEGER,
10915     &setup_input.joy.xright,                    ".joy.xright"
10916   },
10917   {
10918     TYPE_INTEGER,
10919     &setup_input.joy.yupper,                    ".joy.yupper"
10920   },
10921   {
10922     TYPE_INTEGER,
10923     &setup_input.joy.ymiddle,                   ".joy.ymiddle"
10924   },
10925   {
10926     TYPE_INTEGER,
10927     &setup_input.joy.ylower,                    ".joy.ylower"
10928   },
10929   {
10930     TYPE_INTEGER,
10931     &setup_input.joy.snap,                      ".joy.snap_field"
10932   },
10933   {
10934     TYPE_INTEGER,
10935     &setup_input.joy.drop,                      ".joy.place_bomb"
10936   },
10937   {
10938     TYPE_KEY_X11,
10939     &setup_input.key.left,                      ".key.move_left"
10940   },
10941   {
10942     TYPE_KEY_X11,
10943     &setup_input.key.right,                     ".key.move_right"
10944   },
10945   {
10946     TYPE_KEY_X11,
10947     &setup_input.key.up,                        ".key.move_up"
10948   },
10949   {
10950     TYPE_KEY_X11,
10951     &setup_input.key.down,                      ".key.move_down"
10952   },
10953   {
10954     TYPE_KEY_X11,
10955     &setup_input.key.snap,                      ".key.snap_field"
10956   },
10957   {
10958     TYPE_KEY_X11,
10959     &setup_input.key.drop,                      ".key.place_bomb"
10960   },
10961 };
10962
10963 static struct TokenInfo system_setup_tokens[] =
10964 {
10965   {
10966     TYPE_STRING,
10967     &setup.system.sdl_renderdriver,             "system.sdl_renderdriver"
10968   },
10969   {
10970     TYPE_STRING,
10971     &setup.system.sdl_videodriver,              "system.sdl_videodriver"
10972   },
10973   {
10974     TYPE_STRING,
10975     &setup.system.sdl_audiodriver,              "system.sdl_audiodriver"
10976   },
10977   {
10978     TYPE_INTEGER,
10979     &setup.system.audio_fragment_size,          "system.audio_fragment_size"
10980   },
10981 };
10982
10983 static struct TokenInfo internal_setup_tokens[] =
10984 {
10985   {
10986     TYPE_STRING,
10987     &setup.internal.program_title,              "program_title"
10988   },
10989   {
10990     TYPE_STRING,
10991     &setup.internal.program_version,            "program_version"
10992   },
10993   {
10994     TYPE_STRING,
10995     &setup.internal.program_author,             "program_author"
10996   },
10997   {
10998     TYPE_STRING,
10999     &setup.internal.program_email,              "program_email"
11000   },
11001   {
11002     TYPE_STRING,
11003     &setup.internal.program_website,            "program_website"
11004   },
11005   {
11006     TYPE_STRING,
11007     &setup.internal.program_copyright,          "program_copyright"
11008   },
11009   {
11010     TYPE_STRING,
11011     &setup.internal.program_company,            "program_company"
11012   },
11013   {
11014     TYPE_STRING,
11015     &setup.internal.program_icon_file,          "program_icon_file"
11016   },
11017   {
11018     TYPE_STRING,
11019     &setup.internal.default_graphics_set,       "default_graphics_set"
11020   },
11021   {
11022     TYPE_STRING,
11023     &setup.internal.default_sounds_set,         "default_sounds_set"
11024   },
11025   {
11026     TYPE_STRING,
11027     &setup.internal.default_music_set,          "default_music_set"
11028   },
11029   {
11030     TYPE_STRING,
11031     &setup.internal.fallback_graphics_file,     "fallback_graphics_file"
11032   },
11033   {
11034     TYPE_STRING,
11035     &setup.internal.fallback_sounds_file,       "fallback_sounds_file"
11036   },
11037   {
11038     TYPE_STRING,
11039     &setup.internal.fallback_music_file,        "fallback_music_file"
11040   },
11041   {
11042     TYPE_STRING,
11043     &setup.internal.default_level_series,       "default_level_series"
11044   },
11045   {
11046     TYPE_INTEGER,
11047     &setup.internal.default_window_width,       "default_window_width"
11048   },
11049   {
11050     TYPE_INTEGER,
11051     &setup.internal.default_window_height,      "default_window_height"
11052   },
11053   {
11054     TYPE_BOOLEAN,
11055     &setup.internal.choose_from_top_leveldir,   "choose_from_top_leveldir"
11056   },
11057   {
11058     TYPE_BOOLEAN,
11059     &setup.internal.show_scaling_in_title,      "show_scaling_in_title"
11060   },
11061   {
11062     TYPE_BOOLEAN,
11063     &setup.internal.create_user_levelset,       "create_user_levelset"
11064   },
11065   {
11066     TYPE_BOOLEAN,
11067     &setup.internal.info_screens_from_main,     "info_screens_from_main"
11068   },
11069   {
11070     TYPE_BOOLEAN,
11071     &setup.internal.menu_game,                  "menu_game"
11072   },
11073   {
11074     TYPE_BOOLEAN,
11075     &setup.internal.menu_engines,               "menu_engines"
11076   },
11077   {
11078     TYPE_BOOLEAN,
11079     &setup.internal.menu_editor,                "menu_editor"
11080   },
11081   {
11082     TYPE_BOOLEAN,
11083     &setup.internal.menu_graphics,              "menu_graphics"
11084   },
11085   {
11086     TYPE_BOOLEAN,
11087     &setup.internal.menu_sound,                 "menu_sound"
11088   },
11089   {
11090     TYPE_BOOLEAN,
11091     &setup.internal.menu_artwork,               "menu_artwork"
11092   },
11093   {
11094     TYPE_BOOLEAN,
11095     &setup.internal.menu_input,                 "menu_input"
11096   },
11097   {
11098     TYPE_BOOLEAN,
11099     &setup.internal.menu_touch,                 "menu_touch"
11100   },
11101   {
11102     TYPE_BOOLEAN,
11103     &setup.internal.menu_shortcuts,             "menu_shortcuts"
11104   },
11105   {
11106     TYPE_BOOLEAN,
11107     &setup.internal.menu_exit,                  "menu_exit"
11108   },
11109   {
11110     TYPE_BOOLEAN,
11111     &setup.internal.menu_save_and_exit,         "menu_save_and_exit"
11112   },
11113   {
11114     TYPE_BOOLEAN,
11115     &setup.internal.menu_shortcuts_various,     "menu_shortcuts_various"
11116   },
11117   {
11118     TYPE_BOOLEAN,
11119     &setup.internal.menu_shortcuts_focus,       "menu_shortcuts_focus"
11120   },
11121   {
11122     TYPE_BOOLEAN,
11123     &setup.internal.menu_shortcuts_tape,        "menu_shortcuts_tape"
11124   },
11125   {
11126     TYPE_BOOLEAN,
11127     &setup.internal.menu_shortcuts_sound,       "menu_shortcuts_sound"
11128   },
11129   {
11130     TYPE_BOOLEAN,
11131     &setup.internal.menu_shortcuts_snap,        "menu_shortcuts_snap"
11132   },
11133   {
11134     TYPE_BOOLEAN,
11135     &setup.internal.info_title,                 "info_title"
11136   },
11137   {
11138     TYPE_BOOLEAN,
11139     &setup.internal.info_elements,              "info_elements"
11140   },
11141   {
11142     TYPE_BOOLEAN,
11143     &setup.internal.info_music,                 "info_music"
11144   },
11145   {
11146     TYPE_BOOLEAN,
11147     &setup.internal.info_credits,               "info_credits"
11148   },
11149   {
11150     TYPE_BOOLEAN,
11151     &setup.internal.info_program,               "info_program"
11152   },
11153   {
11154     TYPE_BOOLEAN,
11155     &setup.internal.info_version,               "info_version"
11156   },
11157   {
11158     TYPE_BOOLEAN,
11159     &setup.internal.info_levelset,              "info_levelset"
11160   },
11161   {
11162     TYPE_BOOLEAN,
11163     &setup.internal.info_exit,                  "info_exit"
11164   },
11165 };
11166
11167 static struct TokenInfo debug_setup_tokens[] =
11168 {
11169   {
11170     TYPE_INTEGER,
11171     &setup.debug.frame_delay[0],                "debug.frame_delay_0"
11172   },
11173   {
11174     TYPE_INTEGER,
11175     &setup.debug.frame_delay[1],                "debug.frame_delay_1"
11176   },
11177   {
11178     TYPE_INTEGER,
11179     &setup.debug.frame_delay[2],                "debug.frame_delay_2"
11180   },
11181   {
11182     TYPE_INTEGER,
11183     &setup.debug.frame_delay[3],                "debug.frame_delay_3"
11184   },
11185   {
11186     TYPE_INTEGER,
11187     &setup.debug.frame_delay[4],                "debug.frame_delay_4"
11188   },
11189   {
11190     TYPE_INTEGER,
11191     &setup.debug.frame_delay[5],                "debug.frame_delay_5"
11192   },
11193   {
11194     TYPE_INTEGER,
11195     &setup.debug.frame_delay[6],                "debug.frame_delay_6"
11196   },
11197   {
11198     TYPE_INTEGER,
11199     &setup.debug.frame_delay[7],                "debug.frame_delay_7"
11200   },
11201   {
11202     TYPE_INTEGER,
11203     &setup.debug.frame_delay[8],                "debug.frame_delay_8"
11204   },
11205   {
11206     TYPE_INTEGER,
11207     &setup.debug.frame_delay[9],                "debug.frame_delay_9"
11208   },
11209   {
11210     TYPE_KEY_X11,
11211     &setup.debug.frame_delay_key[0],            "debug.key.frame_delay_0"
11212   },
11213   {
11214     TYPE_KEY_X11,
11215     &setup.debug.frame_delay_key[1],            "debug.key.frame_delay_1"
11216   },
11217   {
11218     TYPE_KEY_X11,
11219     &setup.debug.frame_delay_key[2],            "debug.key.frame_delay_2"
11220   },
11221   {
11222     TYPE_KEY_X11,
11223     &setup.debug.frame_delay_key[3],            "debug.key.frame_delay_3"
11224   },
11225   {
11226     TYPE_KEY_X11,
11227     &setup.debug.frame_delay_key[4],            "debug.key.frame_delay_4"
11228   },
11229   {
11230     TYPE_KEY_X11,
11231     &setup.debug.frame_delay_key[5],            "debug.key.frame_delay_5"
11232   },
11233   {
11234     TYPE_KEY_X11,
11235     &setup.debug.frame_delay_key[6],            "debug.key.frame_delay_6"
11236   },
11237   {
11238     TYPE_KEY_X11,
11239     &setup.debug.frame_delay_key[7],            "debug.key.frame_delay_7"
11240   },
11241   {
11242     TYPE_KEY_X11,
11243     &setup.debug.frame_delay_key[8],            "debug.key.frame_delay_8"
11244   },
11245   {
11246     TYPE_KEY_X11,
11247     &setup.debug.frame_delay_key[9],            "debug.key.frame_delay_9"
11248   },
11249   {
11250     TYPE_BOOLEAN,
11251     &setup.debug.frame_delay_use_mod_key,       "debug.frame_delay.use_mod_key"},
11252   {
11253     TYPE_BOOLEAN,
11254     &setup.debug.frame_delay_game_only,         "debug.frame_delay.game_only"
11255   },
11256   {
11257     TYPE_BOOLEAN,
11258     &setup.debug.show_frames_per_second,        "debug.show_frames_per_second"
11259   },
11260   {
11261     TYPE_SWITCH3,
11262     &setup.debug.xsn_mode,                      "debug.xsn_mode"
11263   },
11264   {
11265     TYPE_INTEGER,
11266     &setup.debug.xsn_percent,                   "debug.xsn_percent"
11267   },
11268 };
11269
11270 static struct TokenInfo options_setup_tokens[] =
11271 {
11272   {
11273     TYPE_BOOLEAN,
11274     &setup.options.verbose,                     "options.verbose"
11275   },
11276   {
11277     TYPE_BOOLEAN,
11278     &setup.options.debug,                       "options.debug"
11279   },
11280   {
11281     TYPE_STRING,
11282     &setup.options.debug_mode,                  "options.debug_mode"
11283   },
11284 };
11285
11286 static void setSetupInfoToDefaults(struct SetupInfo *si)
11287 {
11288   int i;
11289
11290   si->player_name = getStringCopy(getDefaultUserName(user.nr));
11291
11292   si->multiple_users = TRUE;
11293
11294   si->sound = TRUE;
11295   si->sound_loops = TRUE;
11296   si->sound_music = TRUE;
11297   si->sound_simple = TRUE;
11298   si->toons = TRUE;
11299   si->global_animations = TRUE;
11300   si->scroll_delay = TRUE;
11301   si->forced_scroll_delay = FALSE;
11302   si->scroll_delay_value = STD_SCROLL_DELAY;
11303   si->engine_snapshot_mode = getStringCopy(STR_SNAPSHOT_MODE_DEFAULT);
11304   si->engine_snapshot_memory = SNAPSHOT_MEMORY_DEFAULT;
11305   si->fade_screens = TRUE;
11306   si->autorecord = TRUE;
11307   si->autorecord_after_replay = TRUE;
11308   si->auto_pause_on_start = FALSE;
11309   si->show_titlescreen = TRUE;
11310   si->quick_doors = FALSE;
11311   si->team_mode = FALSE;
11312   si->handicap = TRUE;
11313   si->skip_levels = TRUE;
11314   si->increment_levels = TRUE;
11315   si->auto_play_next_level = TRUE;
11316   si->count_score_after_game = TRUE;
11317   si->show_scores_after_game = TRUE;
11318   si->time_limit = TRUE;
11319   si->fullscreen = FALSE;
11320   si->window_scaling_percent = STD_WINDOW_SCALING_PERCENT;
11321   si->window_scaling_quality = getStringCopy(SCALING_QUALITY_DEFAULT);
11322   si->screen_rendering_mode = getStringCopy(STR_SPECIAL_RENDERING_DEFAULT);
11323   si->vsync_mode = getStringCopy(STR_VSYNC_MODE_DEFAULT);
11324   si->ask_on_escape = TRUE;
11325   si->ask_on_escape_editor = TRUE;
11326   si->ask_on_game_over = TRUE;
11327   si->ask_on_quit_game = TRUE;
11328   si->ask_on_quit_program = TRUE;
11329   si->quick_switch = FALSE;
11330   si->input_on_focus = FALSE;
11331   si->prefer_aga_graphics = TRUE;
11332   si->prefer_lowpass_sounds = FALSE;
11333   si->prefer_extra_panel_items = TRUE;
11334   si->game_speed_extended = FALSE;
11335   si->game_frame_delay = GAME_FRAME_DELAY;
11336   si->bd_skip_uncovering = FALSE;
11337   si->bd_skip_hatching = FALSE;
11338   si->bd_scroll_delay = TRUE;
11339   si->bd_smooth_movements = AUTO;
11340   si->sp_show_border_elements = FALSE;
11341   si->small_game_graphics = FALSE;
11342   si->show_load_save_buttons = FALSE;
11343   si->show_undo_redo_buttons = FALSE;
11344   si->scores_in_highscore_list = getStringCopy(STR_SCORES_TYPE_DEFAULT);
11345
11346   si->graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11347   si->sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11348   si->music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11349
11350   si->override_level_graphics = FALSE;
11351   si->override_level_sounds = FALSE;
11352   si->override_level_music = FALSE;
11353
11354   si->volume_simple = 100;              // percent
11355   si->volume_loops = 100;               // percent
11356   si->volume_music = 100;               // percent
11357
11358   si->network_mode = FALSE;
11359   si->network_player_nr = 0;            // first player
11360   si->network_server_hostname = getStringCopy(STR_NETWORK_AUTO_DETECT);
11361
11362   si->touch.control_type = getStringCopy(TOUCH_CONTROL_DEFAULT);
11363   si->touch.move_distance = TOUCH_MOVE_DISTANCE_DEFAULT;        // percent
11364   si->touch.drop_distance = TOUCH_DROP_DISTANCE_DEFAULT;        // percent
11365   si->touch.transparency = TOUCH_TRANSPARENCY_DEFAULT;          // percent
11366   si->touch.draw_outlined = TRUE;
11367   si->touch.draw_pressed = TRUE;
11368
11369   for (i = 0; i < 2; i++)
11370   {
11371     char *default_grid_button[6][2] =
11372     {
11373       { "      ", "  ^^  " },
11374       { "      ", "  ^^  " },
11375       { "      ", "<<  >>" },
11376       { "      ", "<<  >>" },
11377       { "111222", "  vv  " },
11378       { "111222", "  vv  " }
11379     };
11380     int grid_xsize = DEFAULT_GRID_XSIZE(i);
11381     int grid_ysize = DEFAULT_GRID_YSIZE(i);
11382     int min_xsize = MIN(6, grid_xsize);
11383     int min_ysize = MIN(6, grid_ysize);
11384     int startx = grid_xsize - min_xsize;
11385     int starty = grid_ysize - min_ysize;
11386     int x, y;
11387
11388     // virtual buttons grid can only be set to defaults if video is initialized
11389     // (this will be repeated if virtual buttons are not loaded from setup file)
11390     if (video.initialized)
11391     {
11392       si->touch.grid_xsize[i] = grid_xsize;
11393       si->touch.grid_ysize[i] = grid_ysize;
11394     }
11395     else
11396     {
11397       si->touch.grid_xsize[i] = -1;
11398       si->touch.grid_ysize[i] = -1;
11399     }
11400
11401     for (x = 0; x < MAX_GRID_XSIZE; x++)
11402       for (y = 0; y < MAX_GRID_YSIZE; y++)
11403         si->touch.grid_button[i][x][y] = CHAR_GRID_BUTTON_NONE;
11404
11405     for (x = 0; x < min_xsize; x++)
11406       for (y = 0; y < min_ysize; y++)
11407         si->touch.grid_button[i][x][starty + y] =
11408           default_grid_button[y][0][x];
11409
11410     for (x = 0; x < min_xsize; x++)
11411       for (y = 0; y < min_ysize; y++)
11412         si->touch.grid_button[i][startx + x][starty + y] =
11413           default_grid_button[y][1][x];
11414   }
11415
11416   si->touch.grid_initialized            = video.initialized;
11417
11418   si->touch.overlay_buttons             = FALSE;
11419
11420   si->editor.el_boulderdash             = TRUE;
11421   si->editor.el_boulderdash_native      = TRUE;
11422   si->editor.el_boulderdash_effects     = TRUE;
11423   si->editor.el_emerald_mine            = TRUE;
11424   si->editor.el_emerald_mine_club       = TRUE;
11425   si->editor.el_more                    = TRUE;
11426   si->editor.el_sokoban                 = TRUE;
11427   si->editor.el_supaplex                = TRUE;
11428   si->editor.el_diamond_caves           = TRUE;
11429   si->editor.el_dx_boulderdash          = TRUE;
11430
11431   si->editor.el_mirror_magic            = TRUE;
11432   si->editor.el_deflektor               = TRUE;
11433
11434   si->editor.el_chars                   = TRUE;
11435   si->editor.el_steel_chars             = TRUE;
11436
11437   si->editor.el_classic                 = TRUE;
11438   si->editor.el_custom                  = TRUE;
11439
11440   si->editor.el_user_defined            = FALSE;
11441   si->editor.el_dynamic                 = TRUE;
11442
11443   si->editor.el_headlines               = TRUE;
11444
11445   si->editor.show_element_token         = FALSE;
11446
11447   si->editor.show_read_only_warning     = TRUE;
11448
11449   si->editor.use_template_for_new_levels = TRUE;
11450
11451   si->shortcut.save_game        = DEFAULT_KEY_SAVE_GAME;
11452   si->shortcut.load_game        = DEFAULT_KEY_LOAD_GAME;
11453   si->shortcut.restart_game     = DEFAULT_KEY_RESTART_GAME;
11454   si->shortcut.pause_before_end = DEFAULT_KEY_PAUSE_BEFORE_END;
11455   si->shortcut.toggle_pause     = DEFAULT_KEY_TOGGLE_PAUSE;
11456
11457   si->shortcut.focus_player[0]  = DEFAULT_KEY_FOCUS_PLAYER_1;
11458   si->shortcut.focus_player[1]  = DEFAULT_KEY_FOCUS_PLAYER_2;
11459   si->shortcut.focus_player[2]  = DEFAULT_KEY_FOCUS_PLAYER_3;
11460   si->shortcut.focus_player[3]  = DEFAULT_KEY_FOCUS_PLAYER_4;
11461   si->shortcut.focus_player_all = DEFAULT_KEY_FOCUS_PLAYER_ALL;
11462
11463   si->shortcut.tape_eject       = DEFAULT_KEY_TAPE_EJECT;
11464   si->shortcut.tape_extra       = DEFAULT_KEY_TAPE_EXTRA;
11465   si->shortcut.tape_stop        = DEFAULT_KEY_TAPE_STOP;
11466   si->shortcut.tape_pause       = DEFAULT_KEY_TAPE_PAUSE;
11467   si->shortcut.tape_record      = DEFAULT_KEY_TAPE_RECORD;
11468   si->shortcut.tape_play        = DEFAULT_KEY_TAPE_PLAY;
11469
11470   si->shortcut.sound_simple     = DEFAULT_KEY_SOUND_SIMPLE;
11471   si->shortcut.sound_loops      = DEFAULT_KEY_SOUND_LOOPS;
11472   si->shortcut.sound_music      = DEFAULT_KEY_SOUND_MUSIC;
11473
11474   si->shortcut.snap_left        = DEFAULT_KEY_SNAP_LEFT;
11475   si->shortcut.snap_right       = DEFAULT_KEY_SNAP_RIGHT;
11476   si->shortcut.snap_up          = DEFAULT_KEY_SNAP_UP;
11477   si->shortcut.snap_down        = DEFAULT_KEY_SNAP_DOWN;
11478
11479   for (i = 0; i < MAX_PLAYERS; i++)
11480   {
11481     si->input[i].use_joystick = FALSE;
11482     si->input[i].joy.device_name = getStringCopy(getDeviceNameFromJoystickNr(i));
11483     si->input[i].joy.xleft   = JOYSTICK_XLEFT;
11484     si->input[i].joy.xmiddle = JOYSTICK_XMIDDLE;
11485     si->input[i].joy.xright  = JOYSTICK_XRIGHT;
11486     si->input[i].joy.yupper  = JOYSTICK_YUPPER;
11487     si->input[i].joy.ymiddle = JOYSTICK_YMIDDLE;
11488     si->input[i].joy.ylower  = JOYSTICK_YLOWER;
11489     si->input[i].joy.snap  = (i == 0 ? JOY_BUTTON_1 : 0);
11490     si->input[i].joy.drop  = (i == 0 ? JOY_BUTTON_2 : 0);
11491     si->input[i].key.left  = (i == 0 ? DEFAULT_KEY_LEFT  : KSYM_UNDEFINED);
11492     si->input[i].key.right = (i == 0 ? DEFAULT_KEY_RIGHT : KSYM_UNDEFINED);
11493     si->input[i].key.up    = (i == 0 ? DEFAULT_KEY_UP    : KSYM_UNDEFINED);
11494     si->input[i].key.down  = (i == 0 ? DEFAULT_KEY_DOWN  : KSYM_UNDEFINED);
11495     si->input[i].key.snap  = (i == 0 ? DEFAULT_KEY_SNAP  : KSYM_UNDEFINED);
11496     si->input[i].key.drop  = (i == 0 ? DEFAULT_KEY_DROP  : KSYM_UNDEFINED);
11497   }
11498
11499   si->system.sdl_renderdriver = getStringCopy(ARG_DEFAULT);
11500   si->system.sdl_videodriver = getStringCopy(ARG_DEFAULT);
11501   si->system.sdl_audiodriver = getStringCopy(ARG_DEFAULT);
11502   si->system.audio_fragment_size = DEFAULT_AUDIO_FRAGMENT_SIZE;
11503
11504   si->internal.program_title     = getStringCopy(PROGRAM_TITLE_STRING);
11505   si->internal.program_version   = getStringCopy(getProgramRealVersionString());
11506   si->internal.program_author    = getStringCopy(PROGRAM_AUTHOR_STRING);
11507   si->internal.program_email     = getStringCopy(PROGRAM_EMAIL_STRING);
11508   si->internal.program_website   = getStringCopy(PROGRAM_WEBSITE_STRING);
11509   si->internal.program_copyright = getStringCopy(PROGRAM_COPYRIGHT_STRING);
11510   si->internal.program_company   = getStringCopy(PROGRAM_COMPANY_STRING);
11511
11512   si->internal.program_icon_file = getStringCopy(PROGRAM_ICON_FILENAME);
11513
11514   si->internal.default_graphics_set = getStringCopy(GFX_CLASSIC_SUBDIR);
11515   si->internal.default_sounds_set   = getStringCopy(SND_CLASSIC_SUBDIR);
11516   si->internal.default_music_set    = getStringCopy(MUS_CLASSIC_SUBDIR);
11517
11518   si->internal.fallback_graphics_file = getStringCopy(UNDEFINED_FILENAME);
11519   si->internal.fallback_sounds_file   = getStringCopy(UNDEFINED_FILENAME);
11520   si->internal.fallback_music_file    = getStringCopy(UNDEFINED_FILENAME);
11521
11522   si->internal.default_level_series = getStringCopy(UNDEFINED_LEVELSET);
11523   si->internal.choose_from_top_leveldir = FALSE;
11524   si->internal.show_scaling_in_title = TRUE;
11525   si->internal.create_user_levelset = TRUE;
11526   si->internal.info_screens_from_main = FALSE;
11527
11528   si->internal.default_window_width  = WIN_XSIZE_DEFAULT;
11529   si->internal.default_window_height = WIN_YSIZE_DEFAULT;
11530
11531   si->debug.frame_delay[0] = DEFAULT_FRAME_DELAY_0;
11532   si->debug.frame_delay[1] = DEFAULT_FRAME_DELAY_1;
11533   si->debug.frame_delay[2] = DEFAULT_FRAME_DELAY_2;
11534   si->debug.frame_delay[3] = DEFAULT_FRAME_DELAY_3;
11535   si->debug.frame_delay[4] = DEFAULT_FRAME_DELAY_4;
11536   si->debug.frame_delay[5] = DEFAULT_FRAME_DELAY_5;
11537   si->debug.frame_delay[6] = DEFAULT_FRAME_DELAY_6;
11538   si->debug.frame_delay[7] = DEFAULT_FRAME_DELAY_7;
11539   si->debug.frame_delay[8] = DEFAULT_FRAME_DELAY_8;
11540   si->debug.frame_delay[9] = DEFAULT_FRAME_DELAY_9;
11541
11542   si->debug.frame_delay_key[0] = DEFAULT_KEY_FRAME_DELAY_0;
11543   si->debug.frame_delay_key[1] = DEFAULT_KEY_FRAME_DELAY_1;
11544   si->debug.frame_delay_key[2] = DEFAULT_KEY_FRAME_DELAY_2;
11545   si->debug.frame_delay_key[3] = DEFAULT_KEY_FRAME_DELAY_3;
11546   si->debug.frame_delay_key[4] = DEFAULT_KEY_FRAME_DELAY_4;
11547   si->debug.frame_delay_key[5] = DEFAULT_KEY_FRAME_DELAY_5;
11548   si->debug.frame_delay_key[6] = DEFAULT_KEY_FRAME_DELAY_6;
11549   si->debug.frame_delay_key[7] = DEFAULT_KEY_FRAME_DELAY_7;
11550   si->debug.frame_delay_key[8] = DEFAULT_KEY_FRAME_DELAY_8;
11551   si->debug.frame_delay_key[9] = DEFAULT_KEY_FRAME_DELAY_9;
11552
11553   si->debug.frame_delay_use_mod_key = DEFAULT_FRAME_DELAY_USE_MOD_KEY;
11554   si->debug.frame_delay_game_only   = DEFAULT_FRAME_DELAY_GAME_ONLY;
11555
11556   si->debug.show_frames_per_second = FALSE;
11557
11558   si->debug.xsn_mode = AUTO;
11559   si->debug.xsn_percent = 0;
11560
11561   si->options.verbose = FALSE;
11562   si->options.debug = FALSE;
11563   si->options.debug_mode = getStringCopy(ARG_UNDEFINED_STRING);
11564
11565 #if defined(PLATFORM_ANDROID)
11566   si->fullscreen = TRUE;
11567   si->touch.overlay_buttons = TRUE;
11568 #endif
11569
11570   setHideSetupEntry(&setup.debug.xsn_mode);
11571 }
11572
11573 static void setSetupInfoToDefaults_AutoSetup(struct SetupInfo *si)
11574 {
11575   si->auto_setup.editor_zoom_tilesize = MINI_TILESIZE;
11576 }
11577
11578 static void setSetupInfoToDefaults_ServerSetup(struct SetupInfo *si)
11579 {
11580   si->player_uuid = NULL;       // (will be set later)
11581   si->player_version = 1;       // (will be set later)
11582
11583   si->use_api_server = TRUE;
11584   si->api_server_hostname = getStringCopy(API_SERVER_HOSTNAME);
11585   si->api_server_password = getStringCopy(UNDEFINED_PASSWORD);
11586   si->ask_for_uploading_tapes = TRUE;
11587   si->ask_for_remaining_tapes = FALSE;
11588   si->provide_uploading_tapes = TRUE;
11589   si->ask_for_using_api_server = TRUE;
11590   si->has_remaining_tapes = FALSE;
11591 }
11592
11593 static void setSetupInfoToDefaults_EditorCascade(struct SetupInfo *si)
11594 {
11595   si->editor_cascade.el_bd              = TRUE;
11596   si->editor_cascade.el_bd_native       = TRUE;
11597   si->editor_cascade.el_bd_effects      = FALSE;
11598   si->editor_cascade.el_em              = TRUE;
11599   si->editor_cascade.el_emc             = TRUE;
11600   si->editor_cascade.el_rnd             = TRUE;
11601   si->editor_cascade.el_sb              = TRUE;
11602   si->editor_cascade.el_sp              = TRUE;
11603   si->editor_cascade.el_dc              = TRUE;
11604   si->editor_cascade.el_dx              = TRUE;
11605
11606   si->editor_cascade.el_mm              = TRUE;
11607   si->editor_cascade.el_df              = TRUE;
11608
11609   si->editor_cascade.el_chars           = FALSE;
11610   si->editor_cascade.el_steel_chars     = FALSE;
11611   si->editor_cascade.el_ce              = FALSE;
11612   si->editor_cascade.el_ge              = FALSE;
11613   si->editor_cascade.el_es              = FALSE;
11614   si->editor_cascade.el_ref             = FALSE;
11615   si->editor_cascade.el_user            = FALSE;
11616   si->editor_cascade.el_dynamic         = FALSE;
11617 }
11618
11619 #define MAX_HIDE_SETUP_TOKEN_SIZE               20
11620
11621 static char *getHideSetupToken(void *setup_value)
11622 {
11623   static char hide_setup_token[MAX_HIDE_SETUP_TOKEN_SIZE];
11624
11625   if (setup_value != NULL)
11626     snprintf(hide_setup_token, MAX_HIDE_SETUP_TOKEN_SIZE, "%p", setup_value);
11627
11628   return hide_setup_token;
11629 }
11630
11631 void setHideSetupEntry(void *setup_value)
11632 {
11633   char *hide_setup_token = getHideSetupToken(setup_value);
11634
11635   if (hide_setup_hash == NULL)
11636     hide_setup_hash = newSetupFileHash();
11637
11638   if (setup_value != NULL)
11639     setHashEntry(hide_setup_hash, hide_setup_token, "");
11640 }
11641
11642 void removeHideSetupEntry(void *setup_value)
11643 {
11644   char *hide_setup_token = getHideSetupToken(setup_value);
11645
11646   if (setup_value != NULL)
11647     removeHashEntry(hide_setup_hash, hide_setup_token);
11648 }
11649
11650 boolean hideSetupEntry(void *setup_value)
11651 {
11652   char *hide_setup_token = getHideSetupToken(setup_value);
11653
11654   return (setup_value != NULL &&
11655           getHashEntry(hide_setup_hash, hide_setup_token) != NULL);
11656 }
11657
11658 static void setSetupInfoFromTokenText(SetupFileHash *setup_file_hash,
11659                                       struct TokenInfo *token_info,
11660                                       int token_nr, char *token_text)
11661 {
11662   char *token_hide_text = getStringCat2(token_text, ".hide");
11663   char *token_hide_value = getHashEntry(setup_file_hash, token_hide_text);
11664
11665   // set the value of this setup option in the setup option structure
11666   setSetupInfo(token_info, token_nr, getHashEntry(setup_file_hash, token_text));
11667
11668   // check if this setup option should be hidden in the setup menu
11669   if (token_hide_value != NULL && get_boolean_from_string(token_hide_value))
11670     setHideSetupEntry(token_info[token_nr].value);
11671
11672   free(token_hide_text);
11673 }
11674
11675 static void setSetupInfoFromTokenInfo(SetupFileHash *setup_file_hash,
11676                                       struct TokenInfo *token_info,
11677                                       int token_nr)
11678 {
11679   setSetupInfoFromTokenText(setup_file_hash, token_info, token_nr,
11680                             token_info[token_nr].text);
11681 }
11682
11683 static void decodeSetupFileHash_Default(SetupFileHash *setup_file_hash)
11684 {
11685   int i, pnr;
11686
11687   if (!setup_file_hash)
11688     return;
11689
11690   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
11691     setSetupInfoFromTokenInfo(setup_file_hash, global_setup_tokens, i);
11692
11693   setup.touch.grid_initialized = TRUE;
11694   for (i = 0; i < 2; i++)
11695   {
11696     int grid_xsize = setup.touch.grid_xsize[i];
11697     int grid_ysize = setup.touch.grid_ysize[i];
11698     int x, y;
11699
11700     // if virtual buttons are not loaded from setup file, repeat initializing
11701     // virtual buttons grid with default values later when video is initialized
11702     if (grid_xsize == -1 ||
11703         grid_ysize == -1)
11704     {
11705       setup.touch.grid_initialized = FALSE;
11706
11707       continue;
11708     }
11709
11710     for (y = 0; y < grid_ysize; y++)
11711     {
11712       char token_string[MAX_LINE_LEN];
11713
11714       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
11715
11716       char *value_string = getHashEntry(setup_file_hash, token_string);
11717
11718       if (value_string == NULL)
11719         continue;
11720
11721       for (x = 0; x < grid_xsize; x++)
11722       {
11723         char c = value_string[x];
11724
11725         setup.touch.grid_button[i][x][y] =
11726           (c == '.' ? CHAR_GRID_BUTTON_NONE : c);
11727       }
11728     }
11729   }
11730
11731   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
11732     setSetupInfoFromTokenInfo(setup_file_hash, editor_setup_tokens, i);
11733
11734   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
11735     setSetupInfoFromTokenInfo(setup_file_hash, shortcut_setup_tokens, i);
11736
11737   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
11738   {
11739     char prefix[30];
11740
11741     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
11742
11743     setup_input = setup.input[pnr];
11744     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
11745     {
11746       char full_token[100];
11747
11748       sprintf(full_token, "%s%s", prefix, player_setup_tokens[i].text);
11749       setSetupInfoFromTokenText(setup_file_hash, player_setup_tokens, i,
11750                                 full_token);
11751     }
11752     setup.input[pnr] = setup_input;
11753   }
11754
11755   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
11756     setSetupInfoFromTokenInfo(setup_file_hash, system_setup_tokens, i);
11757
11758   for (i = 0; i < ARRAY_SIZE(internal_setup_tokens); i++)
11759     setSetupInfoFromTokenInfo(setup_file_hash, internal_setup_tokens, i);
11760
11761   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
11762     setSetupInfoFromTokenInfo(setup_file_hash, debug_setup_tokens, i);
11763
11764   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
11765     setSetupInfoFromTokenInfo(setup_file_hash, options_setup_tokens, i);
11766
11767   setHideRelatedSetupEntries();
11768 }
11769
11770 static void decodeSetupFileHash_AutoSetup(SetupFileHash *setup_file_hash)
11771 {
11772   int i;
11773
11774   if (!setup_file_hash)
11775     return;
11776
11777   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
11778     setSetupInfo(auto_setup_tokens, i,
11779                  getHashEntry(setup_file_hash,
11780                               auto_setup_tokens[i].text));
11781 }
11782
11783 static void decodeSetupFileHash_ServerSetup(SetupFileHash *setup_file_hash)
11784 {
11785   int i;
11786
11787   if (!setup_file_hash)
11788     return;
11789
11790   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
11791     setSetupInfo(server_setup_tokens, i,
11792                  getHashEntry(setup_file_hash,
11793                               server_setup_tokens[i].text));
11794 }
11795
11796 static void decodeSetupFileHash_EditorCascade(SetupFileHash *setup_file_hash)
11797 {
11798   int i;
11799
11800   if (!setup_file_hash)
11801     return;
11802
11803   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
11804     setSetupInfo(editor_cascade_setup_tokens, i,
11805                  getHashEntry(setup_file_hash,
11806                               editor_cascade_setup_tokens[i].text));
11807 }
11808
11809 void LoadUserNames(void)
11810 {
11811   int last_user_nr = user.nr;
11812   int i;
11813
11814   if (global.user_names != NULL)
11815   {
11816     for (i = 0; i < MAX_PLAYER_NAMES; i++)
11817       checked_free(global.user_names[i]);
11818
11819     checked_free(global.user_names);
11820   }
11821
11822   global.user_names = checked_calloc(MAX_PLAYER_NAMES * sizeof(char *));
11823
11824   for (i = 0; i < MAX_PLAYER_NAMES; i++)
11825   {
11826     user.nr = i;
11827
11828     SetupFileHash *setup_file_hash = loadSetupFileHash(getSetupFilename());
11829
11830     if (setup_file_hash)
11831     {
11832       char *player_name = getHashEntry(setup_file_hash, "player_name");
11833
11834       global.user_names[i] = getFixedUserName(player_name);
11835
11836       freeSetupFileHash(setup_file_hash);
11837     }
11838
11839     if (global.user_names[i] == NULL)
11840       global.user_names[i] = getStringCopy(getDefaultUserName(i));
11841   }
11842
11843   user.nr = last_user_nr;
11844 }
11845
11846 void LoadSetupFromFilename(char *filename)
11847 {
11848   SetupFileHash *setup_file_hash = loadSetupFileHash(filename);
11849
11850   if (setup_file_hash)
11851   {
11852     decodeSetupFileHash_Default(setup_file_hash);
11853
11854     freeSetupFileHash(setup_file_hash);
11855   }
11856   else
11857   {
11858     Debug("setup", "using default setup values");
11859   }
11860 }
11861
11862 static void LoadSetup_SpecialPostProcessing(void)
11863 {
11864   char *player_name_new;
11865
11866   // needed to work around problems with fixed length strings
11867   player_name_new = getFixedUserName(setup.player_name);
11868   free(setup.player_name);
11869   setup.player_name = player_name_new;
11870
11871   // "scroll_delay: on(3) / off(0)" was replaced by scroll delay value
11872   if (setup.scroll_delay == FALSE)
11873   {
11874     setup.scroll_delay_value = MIN_SCROLL_DELAY;
11875     setup.scroll_delay = TRUE;                  // now always "on"
11876   }
11877
11878   // make sure that scroll delay value stays inside valid range
11879   setup.scroll_delay_value =
11880     MIN(MAX(MIN_SCROLL_DELAY, setup.scroll_delay_value), MAX_SCROLL_DELAY);
11881 }
11882
11883 void LoadSetup_Default(void)
11884 {
11885   char *filename;
11886
11887   // always start with reliable default values
11888   setSetupInfoToDefaults(&setup);
11889
11890   // try to load setup values from default setup file
11891   filename = getDefaultSetupFilename();
11892
11893   if (fileExists(filename))
11894     LoadSetupFromFilename(filename);
11895
11896   // try to load setup values from platform setup file
11897   filename = getPlatformSetupFilename();
11898
11899   if (fileExists(filename))
11900     LoadSetupFromFilename(filename);
11901
11902   // try to load setup values from user setup file
11903   filename = getSetupFilename();
11904
11905   LoadSetupFromFilename(filename);
11906
11907   LoadSetup_SpecialPostProcessing();
11908 }
11909
11910 void LoadSetup_AutoSetup(void)
11911 {
11912   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
11913   SetupFileHash *setup_file_hash = NULL;
11914
11915   // always start with reliable default values
11916   setSetupInfoToDefaults_AutoSetup(&setup);
11917
11918   setup_file_hash = loadSetupFileHash(filename);
11919
11920   if (setup_file_hash)
11921   {
11922     decodeSetupFileHash_AutoSetup(setup_file_hash);
11923
11924     freeSetupFileHash(setup_file_hash);
11925   }
11926
11927   free(filename);
11928 }
11929
11930 void LoadSetup_ServerSetup(void)
11931 {
11932   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
11933   SetupFileHash *setup_file_hash = NULL;
11934
11935   // always start with reliable default values
11936   setSetupInfoToDefaults_ServerSetup(&setup);
11937
11938   setup_file_hash = loadSetupFileHash(filename);
11939
11940   if (setup_file_hash)
11941   {
11942     decodeSetupFileHash_ServerSetup(setup_file_hash);
11943
11944     freeSetupFileHash(setup_file_hash);
11945   }
11946
11947   free(filename);
11948
11949   if (setup.player_uuid == NULL)
11950   {
11951     // player UUID does not yet exist in setup file
11952     setup.player_uuid = getStringCopy(getUUID());
11953     setup.player_version = 2;
11954
11955     SaveSetup_ServerSetup();
11956   }
11957 }
11958
11959 void LoadSetup_EditorCascade(void)
11960 {
11961   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
11962   SetupFileHash *setup_file_hash = NULL;
11963
11964   // always start with reliable default values
11965   setSetupInfoToDefaults_EditorCascade(&setup);
11966
11967   setup_file_hash = loadSetupFileHash(filename);
11968
11969   if (setup_file_hash)
11970   {
11971     decodeSetupFileHash_EditorCascade(setup_file_hash);
11972
11973     freeSetupFileHash(setup_file_hash);
11974   }
11975
11976   free(filename);
11977 }
11978
11979 void LoadSetup(void)
11980 {
11981   LoadSetup_Default();
11982   LoadSetup_AutoSetup();
11983   LoadSetup_ServerSetup();
11984   LoadSetup_EditorCascade();
11985 }
11986
11987 static void addGameControllerMappingToHash(SetupFileHash *mappings_hash,
11988                                            char *mapping_line)
11989 {
11990   char mapping_guid[MAX_LINE_LEN];
11991   char *mapping_start, *mapping_end;
11992
11993   // get GUID from game controller mapping line: copy complete line
11994   strncpy(mapping_guid, mapping_line, MAX_LINE_LEN - 1);
11995   mapping_guid[MAX_LINE_LEN - 1] = '\0';
11996
11997   // get GUID from game controller mapping line: cut after GUID part
11998   mapping_start = strchr(mapping_guid, ',');
11999   if (mapping_start != NULL)
12000     *mapping_start = '\0';
12001
12002   // cut newline from game controller mapping line
12003   mapping_end = strchr(mapping_line, '\n');
12004   if (mapping_end != NULL)
12005     *mapping_end = '\0';
12006
12007   // add mapping entry to game controller mappings hash
12008   setHashEntry(mappings_hash, mapping_guid, mapping_line);
12009 }
12010
12011 static void LoadSetup_ReadGameControllerMappings(SetupFileHash *mappings_hash,
12012                                                  char *filename)
12013 {
12014   FILE *file;
12015
12016   if (!(file = fopen(filename, MODE_READ)))
12017   {
12018     Warn("cannot read game controller mappings file '%s'", filename);
12019
12020     return;
12021   }
12022
12023   while (!feof(file))
12024   {
12025     char line[MAX_LINE_LEN];
12026
12027     if (!fgets(line, MAX_LINE_LEN, file))
12028       break;
12029
12030     addGameControllerMappingToHash(mappings_hash, line);
12031   }
12032
12033   fclose(file);
12034 }
12035
12036 void SaveSetup_Default(void)
12037 {
12038   char *filename = getSetupFilename();
12039   FILE *file;
12040   int i, pnr;
12041
12042   InitUserDataDirectory();
12043
12044   if (!(file = fopen(filename, MODE_WRITE)))
12045   {
12046     Warn("cannot write setup file '%s'", filename);
12047
12048     return;
12049   }
12050
12051   fprintFileHeader(file, SETUP_FILENAME);
12052
12053   for (i = 0; i < ARRAY_SIZE(global_setup_tokens); i++)
12054   {
12055     // just to make things nicer :)
12056     if (global_setup_tokens[i].value == &setup.multiple_users           ||
12057         global_setup_tokens[i].value == &setup.sound                    ||
12058         global_setup_tokens[i].value == &setup.graphics_set             ||
12059         global_setup_tokens[i].value == &setup.volume_simple            ||
12060         global_setup_tokens[i].value == &setup.network_mode             ||
12061         global_setup_tokens[i].value == &setup.touch.control_type       ||
12062         global_setup_tokens[i].value == &setup.touch.grid_xsize[0]      ||
12063         global_setup_tokens[i].value == &setup.touch.grid_xsize[1])
12064       fprintf(file, "\n");
12065
12066     fprintf(file, "%s\n", getSetupLine(global_setup_tokens, "", i));
12067   }
12068
12069   for (i = 0; i < 2; i++)
12070   {
12071     int grid_xsize = setup.touch.grid_xsize[i];
12072     int grid_ysize = setup.touch.grid_ysize[i];
12073     int x, y;
12074
12075     fprintf(file, "\n");
12076
12077     for (y = 0; y < grid_ysize; y++)
12078     {
12079       char token_string[MAX_LINE_LEN];
12080       char value_string[MAX_LINE_LEN];
12081
12082       sprintf(token_string, "touch.virtual_buttons.%d.%02d", i, y);
12083
12084       for (x = 0; x < grid_xsize; x++)
12085       {
12086         char c = setup.touch.grid_button[i][x][y];
12087
12088         value_string[x] = (c == CHAR_GRID_BUTTON_NONE ? '.' : c);
12089       }
12090
12091       value_string[grid_xsize] = '\0';
12092
12093       fprintf(file, "%s\n", getFormattedSetupEntry(token_string, value_string));
12094     }
12095   }
12096
12097   fprintf(file, "\n");
12098   for (i = 0; i < ARRAY_SIZE(editor_setup_tokens); i++)
12099     fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
12100
12101   fprintf(file, "\n");
12102   for (i = 0; i < ARRAY_SIZE(shortcut_setup_tokens); i++)
12103     fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
12104
12105   for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
12106   {
12107     char prefix[30];
12108
12109     sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
12110     fprintf(file, "\n");
12111
12112     setup_input = setup.input[pnr];
12113     for (i = 0; i < ARRAY_SIZE(player_setup_tokens); i++)
12114       fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
12115   }
12116
12117   fprintf(file, "\n");
12118   for (i = 0; i < ARRAY_SIZE(system_setup_tokens); i++)
12119     fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
12120
12121   // (internal setup values not saved to user setup file)
12122
12123   fprintf(file, "\n");
12124   for (i = 0; i < ARRAY_SIZE(debug_setup_tokens); i++)
12125     if (!strPrefix(debug_setup_tokens[i].text, "debug.xsn_") ||
12126         setup.debug.xsn_mode != AUTO)
12127       fprintf(file, "%s\n", getSetupLine(debug_setup_tokens, "", i));
12128
12129   fprintf(file, "\n");
12130   for (i = 0; i < ARRAY_SIZE(options_setup_tokens); i++)
12131     fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
12132
12133   fclose(file);
12134
12135   SetFilePermissions(filename, PERMS_PRIVATE);
12136 }
12137
12138 void SaveSetup_AutoSetup(void)
12139 {
12140   char *filename = getPath2(getSetupDir(), AUTOSETUP_FILENAME);
12141   FILE *file;
12142   int i;
12143
12144   InitUserDataDirectory();
12145
12146   if (!(file = fopen(filename, MODE_WRITE)))
12147   {
12148     Warn("cannot write auto setup file '%s'", filename);
12149
12150     free(filename);
12151
12152     return;
12153   }
12154
12155   fprintFileHeader(file, AUTOSETUP_FILENAME);
12156
12157   for (i = 0; i < ARRAY_SIZE(auto_setup_tokens); i++)
12158     fprintf(file, "%s\n", getSetupLine(auto_setup_tokens, "", i));
12159
12160   fclose(file);
12161
12162   SetFilePermissions(filename, PERMS_PRIVATE);
12163
12164   free(filename);
12165 }
12166
12167 void SaveSetup_ServerSetup(void)
12168 {
12169   char *filename = getPath2(getSetupDir(), SERVERSETUP_FILENAME);
12170   FILE *file;
12171   int i;
12172
12173   InitUserDataDirectory();
12174
12175   if (!(file = fopen(filename, MODE_WRITE)))
12176   {
12177     Warn("cannot write server setup file '%s'", filename);
12178
12179     free(filename);
12180
12181     return;
12182   }
12183
12184   fprintFileHeader(file, SERVERSETUP_FILENAME);
12185
12186   for (i = 0; i < ARRAY_SIZE(server_setup_tokens); i++)
12187   {
12188     // just to make things nicer :)
12189     if (server_setup_tokens[i].value == &setup.use_api_server)
12190       fprintf(file, "\n");
12191
12192     fprintf(file, "%s\n", getSetupLine(server_setup_tokens, "", i));
12193   }
12194
12195   fclose(file);
12196
12197   SetFilePermissions(filename, PERMS_PRIVATE);
12198
12199   free(filename);
12200 }
12201
12202 void SaveSetup_EditorCascade(void)
12203 {
12204   char *filename = getPath2(getSetupDir(), EDITORCASCADE_FILENAME);
12205   FILE *file;
12206   int i;
12207
12208   InitUserDataDirectory();
12209
12210   if (!(file = fopen(filename, MODE_WRITE)))
12211   {
12212     Warn("cannot write editor cascade state file '%s'", filename);
12213
12214     free(filename);
12215
12216     return;
12217   }
12218
12219   fprintFileHeader(file, EDITORCASCADE_FILENAME);
12220
12221   for (i = 0; i < ARRAY_SIZE(editor_cascade_setup_tokens); i++)
12222     fprintf(file, "%s\n", getSetupLine(editor_cascade_setup_tokens, "", i));
12223
12224   fclose(file);
12225
12226   SetFilePermissions(filename, PERMS_PRIVATE);
12227
12228   free(filename);
12229 }
12230
12231 void SaveSetup(void)
12232 {
12233   SaveSetup_Default();
12234   SaveSetup_AutoSetup();
12235   SaveSetup_ServerSetup();
12236   SaveSetup_EditorCascade();
12237 }
12238
12239 static void SaveSetup_WriteGameControllerMappings(SetupFileHash *mappings_hash,
12240                                                   char *filename)
12241 {
12242   FILE *file;
12243
12244   if (!(file = fopen(filename, MODE_WRITE)))
12245   {
12246     Warn("cannot write game controller mappings file '%s'", filename);
12247
12248     return;
12249   }
12250
12251   BEGIN_HASH_ITERATION(mappings_hash, itr)
12252   {
12253     fprintf(file, "%s\n", HASH_ITERATION_VALUE(itr));
12254   }
12255   END_HASH_ITERATION(mappings_hash, itr)
12256
12257   fclose(file);
12258 }
12259
12260 void SaveSetup_AddGameControllerMapping(char *mapping)
12261 {
12262   char *filename = getPath2(getSetupDir(), GAMECONTROLLER_BASENAME);
12263   SetupFileHash *mappings_hash = newSetupFileHash();
12264
12265   InitUserDataDirectory();
12266
12267   // load existing personal game controller mappings
12268   LoadSetup_ReadGameControllerMappings(mappings_hash, filename);
12269
12270   // add new mapping to personal game controller mappings
12271   addGameControllerMappingToHash(mappings_hash, mapping);
12272
12273   // save updated personal game controller mappings
12274   SaveSetup_WriteGameControllerMappings(mappings_hash, filename);
12275
12276   freeSetupFileHash(mappings_hash);
12277   free(filename);
12278 }
12279
12280 void LoadCustomElementDescriptions(void)
12281 {
12282   char *filename = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
12283   SetupFileHash *setup_file_hash;
12284   int i;
12285
12286   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12287   {
12288     if (element_info[i].custom_description != NULL)
12289     {
12290       free(element_info[i].custom_description);
12291       element_info[i].custom_description = NULL;
12292     }
12293   }
12294
12295   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
12296     return;
12297
12298   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
12299   {
12300     char *token = getStringCat2(element_info[i].token_name, ".name");
12301     char *value = getHashEntry(setup_file_hash, token);
12302
12303     if (value != NULL)
12304       element_info[i].custom_description = getStringCopy(value);
12305
12306     free(token);
12307   }
12308
12309   freeSetupFileHash(setup_file_hash);
12310 }
12311
12312 static int getElementFromToken(char *token)
12313 {
12314   char *value = getHashEntry(element_token_hash, token);
12315
12316   if (value != NULL)
12317     return atoi(value);
12318
12319   Warn("unknown element token '%s'", token);
12320
12321   return EL_UNDEFINED;
12322 }
12323
12324 void FreeGlobalAnimEventInfo(void)
12325 {
12326   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12327
12328   if (gaei->event_list == NULL)
12329     return;
12330
12331   int i;
12332
12333   for (i = 0; i < gaei->num_event_lists; i++)
12334   {
12335     checked_free(gaei->event_list[i]->event_value);
12336     checked_free(gaei->event_list[i]);
12337   }
12338
12339   checked_free(gaei->event_list);
12340
12341   gaei->event_list = NULL;
12342   gaei->num_event_lists = 0;
12343 }
12344
12345 static int AddGlobalAnimEventList(void)
12346 {
12347   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12348   int list_pos = gaei->num_event_lists++;
12349
12350   gaei->event_list = checked_realloc(gaei->event_list, gaei->num_event_lists *
12351                                      sizeof(struct GlobalAnimEventListInfo *));
12352
12353   gaei->event_list[list_pos] =
12354     checked_calloc(sizeof(struct GlobalAnimEventListInfo));
12355
12356   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12357
12358   gaeli->event_value = NULL;
12359   gaeli->num_event_values = 0;
12360
12361   return list_pos;
12362 }
12363
12364 static int AddGlobalAnimEventValue(int list_pos, int event_value)
12365 {
12366   // do not add empty global animation events
12367   if (event_value == ANIM_EVENT_NONE)
12368     return list_pos;
12369
12370   // if list position is undefined, create new list
12371   if (list_pos == ANIM_EVENT_UNDEFINED)
12372     list_pos = AddGlobalAnimEventList();
12373
12374   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12375   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12376   int value_pos = gaeli->num_event_values++;
12377
12378   gaeli->event_value = checked_realloc(gaeli->event_value,
12379                                        gaeli->num_event_values * sizeof(int *));
12380
12381   gaeli->event_value[value_pos] = event_value;
12382
12383   return list_pos;
12384 }
12385
12386 int GetGlobalAnimEventValue(int list_pos, int value_pos)
12387 {
12388   if (list_pos == ANIM_EVENT_UNDEFINED)
12389     return ANIM_EVENT_NONE;
12390
12391   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12392   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12393
12394   return gaeli->event_value[value_pos];
12395 }
12396
12397 int GetGlobalAnimEventValueCount(int list_pos)
12398 {
12399   if (list_pos == ANIM_EVENT_UNDEFINED)
12400     return 0;
12401
12402   struct GlobalAnimEventInfo *gaei = &global_anim_event_info;
12403   struct GlobalAnimEventListInfo *gaeli = gaei->event_list[list_pos];
12404
12405   return gaeli->num_event_values;
12406 }
12407
12408 // This function checks if a string <s> of the format "string1, string2, ..."
12409 // exactly contains a string <s_contained>.
12410
12411 static boolean string_has_parameter(char *s, char *s_contained)
12412 {
12413   char *substring;
12414
12415   if (s == NULL || s_contained == NULL)
12416     return FALSE;
12417
12418   if (strlen(s_contained) > strlen(s))
12419     return FALSE;
12420
12421   if (strncmp(s, s_contained, strlen(s_contained)) == 0)
12422   {
12423     char next_char = s[strlen(s_contained)];
12424
12425     // check if next character is delimiter or whitespace
12426     if (next_char == ',' || next_char == '\0' ||
12427         next_char == ' ' || next_char == '\t')
12428       return TRUE;
12429   }
12430
12431   // check if string contains another parameter string after a comma
12432   substring = strchr(s, ',');
12433   if (substring == NULL)        // string does not contain a comma
12434     return FALSE;
12435
12436   // advance string pointer to next character after the comma
12437   substring++;
12438
12439   // skip potential whitespaces after the comma
12440   while (*substring == ' ' || *substring == '\t')
12441     substring++;
12442
12443   return string_has_parameter(substring, s_contained);
12444 }
12445
12446 static int get_anim_parameter_value_ce(char *s)
12447 {
12448   char *s_ptr = s;
12449   char *pattern_1 = "ce_change:custom_";
12450   char *pattern_2 = ".page_";
12451   int pattern_1_len = strlen(pattern_1);
12452   char *matching_char = strstr(s_ptr, pattern_1);
12453   int result = ANIM_EVENT_NONE;
12454
12455   if (matching_char == NULL)
12456     return ANIM_EVENT_NONE;
12457
12458   result = ANIM_EVENT_CE_CHANGE;
12459
12460   s_ptr = matching_char + pattern_1_len;
12461
12462   // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
12463   if (*s_ptr >= '0' && *s_ptr <= '9')
12464   {
12465     int gic_ce_nr = (*s_ptr++ - '0');
12466
12467     if (*s_ptr >= '0' && *s_ptr <= '9')
12468     {
12469       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12470
12471       if (*s_ptr >= '0' && *s_ptr <= '9')
12472         gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
12473     }
12474
12475     if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
12476       return ANIM_EVENT_NONE;
12477
12478     // custom element stored as 0 to 255
12479     gic_ce_nr--;
12480
12481     result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
12482   }
12483   else
12484   {
12485     // invalid custom element number specified
12486
12487     return ANIM_EVENT_NONE;
12488   }
12489
12490   // check for change page number ("page_X" or "page_XX") (optional)
12491   if (strPrefix(s_ptr, pattern_2))
12492   {
12493     s_ptr += strlen(pattern_2);
12494
12495     if (*s_ptr >= '0' && *s_ptr <= '9')
12496     {
12497       int gic_page_nr = (*s_ptr++ - '0');
12498
12499       if (*s_ptr >= '0' && *s_ptr <= '9')
12500         gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
12501
12502       if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
12503         return ANIM_EVENT_NONE;
12504
12505       // change page stored as 1 to 32 (0 means "all change pages")
12506
12507       result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
12508     }
12509     else
12510     {
12511       // invalid animation part number specified
12512
12513       return ANIM_EVENT_NONE;
12514     }
12515   }
12516
12517   // discard result if next character is neither delimiter nor whitespace
12518   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12519         *s_ptr == ' ' || *s_ptr == '\t'))
12520     return ANIM_EVENT_NONE;
12521
12522   return result;
12523 }
12524
12525 static int get_anim_parameter_value(char *s)
12526 {
12527   int event_value[] =
12528   {
12529     ANIM_EVENT_CLICK,
12530     ANIM_EVENT_INIT,
12531     ANIM_EVENT_START,
12532     ANIM_EVENT_END,
12533     ANIM_EVENT_POST
12534   };
12535   char *pattern_1[] =
12536   {
12537     "click:anim_",
12538     "init:anim_",
12539     "start:anim_",
12540     "end:anim_",
12541     "post:anim_"
12542   };
12543   char *pattern_2 = ".part_";
12544   char *matching_char = NULL;
12545   char *s_ptr = s;
12546   int pattern_1_len = 0;
12547   int result = ANIM_EVENT_NONE;
12548   int i;
12549
12550   result = get_anim_parameter_value_ce(s);
12551
12552   if (result != ANIM_EVENT_NONE)
12553     return result;
12554
12555   for (i = 0; i < ARRAY_SIZE(event_value); i++)
12556   {
12557     matching_char = strstr(s_ptr, pattern_1[i]);
12558     pattern_1_len = strlen(pattern_1[i]);
12559     result = event_value[i];
12560
12561     if (matching_char != NULL)
12562       break;
12563   }
12564
12565   if (matching_char == NULL)
12566     return ANIM_EVENT_NONE;
12567
12568   s_ptr = matching_char + pattern_1_len;
12569
12570   // check for main animation number ("anim_X" or "anim_XX")
12571   if (*s_ptr >= '0' && *s_ptr <= '9')
12572   {
12573     int gic_anim_nr = (*s_ptr++ - '0');
12574
12575     if (*s_ptr >= '0' && *s_ptr <= '9')
12576       gic_anim_nr = 10 * gic_anim_nr + (*s_ptr++ - '0');
12577
12578     if (gic_anim_nr < 1 || gic_anim_nr > MAX_GLOBAL_ANIMS)
12579       return ANIM_EVENT_NONE;
12580
12581     result |= gic_anim_nr << ANIM_EVENT_ANIM_BIT;
12582   }
12583   else
12584   {
12585     // invalid main animation number specified
12586
12587     return ANIM_EVENT_NONE;
12588   }
12589
12590   // check for animation part number ("part_X" or "part_XX") (optional)
12591   if (strPrefix(s_ptr, pattern_2))
12592   {
12593     s_ptr += strlen(pattern_2);
12594
12595     if (*s_ptr >= '0' && *s_ptr <= '9')
12596     {
12597       int gic_part_nr = (*s_ptr++ - '0');
12598
12599       if (*s_ptr >= '0' && *s_ptr <= '9')
12600         gic_part_nr = 10 * gic_part_nr + (*s_ptr++ - '0');
12601
12602       if (gic_part_nr < 1 || gic_part_nr > MAX_GLOBAL_ANIM_PARTS)
12603         return ANIM_EVENT_NONE;
12604
12605       result |= gic_part_nr << ANIM_EVENT_PART_BIT;
12606     }
12607     else
12608     {
12609       // invalid animation part number specified
12610
12611       return ANIM_EVENT_NONE;
12612     }
12613   }
12614
12615   // discard result if next character is neither delimiter nor whitespace
12616   if (!(*s_ptr == ',' || *s_ptr == '\0' ||
12617         *s_ptr == ' ' || *s_ptr == '\t'))
12618     return ANIM_EVENT_NONE;
12619
12620   return result;
12621 }
12622
12623 static int get_anim_parameter_values(char *s)
12624 {
12625   int list_pos = ANIM_EVENT_UNDEFINED;
12626   int event_value = ANIM_EVENT_DEFAULT;
12627
12628   if (string_has_parameter(s, "any"))
12629     event_value |= ANIM_EVENT_ANY;
12630
12631   if (string_has_parameter(s, "click:self") ||
12632       string_has_parameter(s, "click") ||
12633       string_has_parameter(s, "self"))
12634     event_value |= ANIM_EVENT_SELF;
12635
12636   if (string_has_parameter(s, "unclick:any"))
12637     event_value |= ANIM_EVENT_UNCLICK_ANY;
12638
12639   // if animation event found, add it to global animation event list
12640   if (event_value != ANIM_EVENT_NONE)
12641     list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12642
12643   while (s != NULL)
12644   {
12645     // add optional "click:anim_X" or "click:anim_X.part_X" parameter
12646     event_value = get_anim_parameter_value(s);
12647
12648     // if animation event found, add it to global animation event list
12649     if (event_value != ANIM_EVENT_NONE)
12650       list_pos = AddGlobalAnimEventValue(list_pos, event_value);
12651
12652     // continue with next part of the string, starting with next comma
12653     s = strchr(s + 1, ',');
12654   }
12655
12656   return list_pos;
12657 }
12658
12659 static int get_anim_action_parameter_value(char *token)
12660 {
12661   // check most common default case first to massively speed things up
12662   if (strEqual(token, ARG_UNDEFINED))
12663     return ANIM_EVENT_ACTION_NONE;
12664
12665   int result = getImageIDFromToken(token);
12666
12667   if (result == -1)
12668   {
12669     char *gfx_token = getStringCat2("gfx.", token);
12670
12671     result = getImageIDFromToken(gfx_token);
12672
12673     checked_free(gfx_token);
12674   }
12675
12676   if (result == -1)
12677   {
12678     Key key = getKeyFromX11KeyName(token);
12679
12680     if (key != KSYM_UNDEFINED)
12681       result = -(int)key;
12682   }
12683
12684   if (result == -1)
12685   {
12686     if (isURL(token))
12687     {
12688       result = get_hash_from_string(token);     // unsigned int => int
12689       result = ABS(result);                     // may be negative now
12690       result += (result < MAX_IMAGE_FILES ? MAX_IMAGE_FILES : 0);
12691
12692       setHashEntry(anim_url_hash, int2str(result, 0), token);
12693     }
12694   }
12695
12696   if (result == -1)
12697     result = ANIM_EVENT_ACTION_NONE;
12698
12699   return result;
12700 }
12701
12702 int get_parameter_value(char *value_raw, char *suffix, int type)
12703 {
12704   char *value = getStringToLower(value_raw);
12705   int result = 0;       // probably a save default value
12706
12707   if (strEqual(suffix, ".direction"))
12708   {
12709     result = (strEqual(value, "left")  ? MV_LEFT :
12710               strEqual(value, "right") ? MV_RIGHT :
12711               strEqual(value, "up")    ? MV_UP :
12712               strEqual(value, "down")  ? MV_DOWN : MV_NONE);
12713   }
12714   else if (strEqual(suffix, ".position"))
12715   {
12716     result = (strEqual(value, "left")   ? POS_LEFT :
12717               strEqual(value, "right")  ? POS_RIGHT :
12718               strEqual(value, "top")    ? POS_TOP :
12719               strEqual(value, "upper")  ? POS_UPPER :
12720               strEqual(value, "middle") ? POS_MIDDLE :
12721               strEqual(value, "lower")  ? POS_LOWER :
12722               strEqual(value, "bottom") ? POS_BOTTOM :
12723               strEqual(value, "any")    ? POS_ANY :
12724               strEqual(value, "ce")     ? POS_CE :
12725               strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
12726               strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
12727   }
12728   else if (strEqual(suffix, ".align"))
12729   {
12730     result = (strEqual(value, "left")   ? ALIGN_LEFT :
12731               strEqual(value, "right")  ? ALIGN_RIGHT :
12732               strEqual(value, "center") ? ALIGN_CENTER :
12733               strEqual(value, "middle") ? ALIGN_CENTER : ALIGN_DEFAULT);
12734   }
12735   else if (strEqual(suffix, ".valign"))
12736   {
12737     result = (strEqual(value, "top")    ? VALIGN_TOP :
12738               strEqual(value, "bottom") ? VALIGN_BOTTOM :
12739               strEqual(value, "middle") ? VALIGN_MIDDLE :
12740               strEqual(value, "center") ? VALIGN_MIDDLE : VALIGN_DEFAULT);
12741   }
12742   else if (strEqual(suffix, ".anim_mode"))
12743   {
12744     result = (string_has_parameter(value, "none")       ? ANIM_NONE :
12745               string_has_parameter(value, "loop")       ? ANIM_LOOP :
12746               string_has_parameter(value, "linear")     ? ANIM_LINEAR :
12747               string_has_parameter(value, "pingpong")   ? ANIM_PINGPONG :
12748               string_has_parameter(value, "pingpong2")  ? ANIM_PINGPONG2 :
12749               string_has_parameter(value, "random")     ? ANIM_RANDOM :
12750               string_has_parameter(value, "random_static") ? ANIM_RANDOM_STATIC :
12751               string_has_parameter(value, "ce_value")   ? ANIM_CE_VALUE :
12752               string_has_parameter(value, "ce_score")   ? ANIM_CE_SCORE :
12753               string_has_parameter(value, "ce_delay")   ? ANIM_CE_DELAY :
12754               string_has_parameter(value, "horizontal") ? ANIM_HORIZONTAL :
12755               string_has_parameter(value, "vertical")   ? ANIM_VERTICAL :
12756               string_has_parameter(value, "centered")   ? ANIM_CENTERED :
12757               string_has_parameter(value, "all")        ? ANIM_ALL :
12758               string_has_parameter(value, "tiled")      ? ANIM_TILED :
12759               string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
12760               ANIM_DEFAULT);
12761
12762     if (string_has_parameter(value, "once"))
12763       result |= ANIM_ONCE;
12764
12765     if (string_has_parameter(value, "reverse"))
12766       result |= ANIM_REVERSE;
12767
12768     if (string_has_parameter(value, "opaque_player"))
12769       result |= ANIM_OPAQUE_PLAYER;
12770
12771     if (string_has_parameter(value, "static_panel"))
12772       result |= ANIM_STATIC_PANEL;
12773   }
12774   else if (strEqual(suffix, ".init_event") ||
12775            strEqual(suffix, ".anim_event"))
12776   {
12777     result = get_anim_parameter_values(value);
12778   }
12779   else if (strEqual(suffix, ".init_delay_action") ||
12780            strEqual(suffix, ".anim_delay_action") ||
12781            strEqual(suffix, ".post_delay_action") ||
12782            strEqual(suffix, ".init_event_action") ||
12783            strEqual(suffix, ".anim_event_action"))
12784   {
12785     result = get_anim_action_parameter_value(value_raw);
12786   }
12787   else if (strEqual(suffix, ".class"))
12788   {
12789     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12790               get_hash_from_string(value));
12791   }
12792   else if (strEqual(suffix, ".style"))
12793   {
12794     result = STYLE_DEFAULT;
12795
12796     if (string_has_parameter(value, "accurate_borders"))
12797       result |= STYLE_ACCURATE_BORDERS;
12798
12799     if (string_has_parameter(value, "inner_corners"))
12800       result |= STYLE_INNER_CORNERS;
12801
12802     if (string_has_parameter(value, "reverse"))
12803       result |= STYLE_REVERSE;
12804
12805     if (string_has_parameter(value, "leftmost_position"))
12806       result |= STYLE_LEFTMOST_POSITION;
12807
12808     if (string_has_parameter(value, "block_clicks"))
12809       result |= STYLE_BLOCK;
12810
12811     if (string_has_parameter(value, "passthrough_clicks"))
12812       result |= STYLE_PASSTHROUGH;
12813
12814     if (string_has_parameter(value, "multiple_actions"))
12815       result |= STYLE_MULTIPLE_ACTIONS;
12816
12817     if (string_has_parameter(value, "consume_ce_event"))
12818       result |= STYLE_CONSUME_CE_EVENT;
12819   }
12820   else if (strEqual(suffix, ".fade_mode"))
12821   {
12822     result = (string_has_parameter(value, "none")       ? FADE_MODE_NONE :
12823               string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
12824               string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
12825               string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
12826               string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
12827               string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
12828               string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
12829               FADE_MODE_DEFAULT);
12830   }
12831   else if (strEqual(suffix, ".auto_delay_unit"))
12832   {
12833     result = (string_has_parameter(value, "ms")     ? AUTO_DELAY_UNIT_MS :
12834               string_has_parameter(value, "frames") ? AUTO_DELAY_UNIT_FRAMES :
12835               AUTO_DELAY_UNIT_DEFAULT);
12836   }
12837   else if (strPrefix(suffix, ".font"))          // (may also be ".font_xyz")
12838   {
12839     result = gfx.get_font_from_token_function(value);
12840   }
12841   else          // generic parameter of type integer or boolean
12842   {
12843     result = (strEqual(value, ARG_UNDEFINED) ? ARG_UNDEFINED_VALUE :
12844               type == TYPE_INTEGER ? get_integer_from_string(value) :
12845               type == TYPE_BOOLEAN ? get_boolean_from_string(value) :
12846               ARG_UNDEFINED_VALUE);
12847   }
12848
12849   free(value);
12850
12851   return result;
12852 }
12853
12854 static int get_token_parameter_value(char *token, char *value_raw)
12855 {
12856   char *suffix;
12857
12858   if (token == NULL || value_raw == NULL)
12859     return ARG_UNDEFINED_VALUE;
12860
12861   suffix = strrchr(token, '.');
12862   if (suffix == NULL)
12863     suffix = token;
12864
12865   if (strEqual(suffix, ".element"))
12866     return getElementFromToken(value_raw);
12867
12868   // !!! USE CORRECT VALUE TYPE (currently works also for TYPE_BOOLEAN) !!!
12869   return get_parameter_value(value_raw, suffix, TYPE_INTEGER);
12870 }
12871
12872 void InitMenuDesignSettings_FromHash(SetupFileHash *setup_file_hash,
12873                                      boolean ignore_defaults)
12874 {
12875   int i;
12876
12877   for (i = 0; image_config_vars[i].token != NULL; i++)
12878   {
12879     char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
12880
12881     // (ignore definitions set to "[DEFAULT]" which are already initialized)
12882     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
12883       continue;
12884
12885     if (value != NULL)
12886       *image_config_vars[i].value =
12887         get_token_parameter_value(image_config_vars[i].token, value);
12888   }
12889 }
12890
12891 void InitMenuDesignSettings_Static(void)
12892 {
12893   // always start with reliable default values from static default config
12894   InitMenuDesignSettings_FromHash(image_config_hash, FALSE);
12895 }
12896
12897 static void InitMenuDesignSettings_SpecialPreProcessing(void)
12898 {
12899   int i;
12900
12901   // the following initializes hierarchical values from static configuration
12902
12903   // special case: initialize "ARG_DEFAULT" values in static default config
12904   // (e.g., initialize "[titlemessage].fade_mode" from "[title].fade_mode")
12905   titlescreen_initial_first_default.fade_mode  =
12906     title_initial_first_default.fade_mode;
12907   titlescreen_initial_first_default.fade_delay =
12908     title_initial_first_default.fade_delay;
12909   titlescreen_initial_first_default.post_delay =
12910     title_initial_first_default.post_delay;
12911   titlescreen_initial_first_default.auto_delay =
12912     title_initial_first_default.auto_delay;
12913   titlescreen_initial_first_default.auto_delay_unit =
12914     title_initial_first_default.auto_delay_unit;
12915   titlescreen_first_default.fade_mode  = title_first_default.fade_mode;
12916   titlescreen_first_default.fade_delay = title_first_default.fade_delay;
12917   titlescreen_first_default.post_delay = title_first_default.post_delay;
12918   titlescreen_first_default.auto_delay = title_first_default.auto_delay;
12919   titlescreen_first_default.auto_delay_unit =
12920     title_first_default.auto_delay_unit;
12921   titlemessage_initial_first_default.fade_mode  =
12922     title_initial_first_default.fade_mode;
12923   titlemessage_initial_first_default.fade_delay =
12924     title_initial_first_default.fade_delay;
12925   titlemessage_initial_first_default.post_delay =
12926     title_initial_first_default.post_delay;
12927   titlemessage_initial_first_default.auto_delay =
12928     title_initial_first_default.auto_delay;
12929   titlemessage_initial_first_default.auto_delay_unit =
12930     title_initial_first_default.auto_delay_unit;
12931   titlemessage_first_default.fade_mode  = title_first_default.fade_mode;
12932   titlemessage_first_default.fade_delay = title_first_default.fade_delay;
12933   titlemessage_first_default.post_delay = title_first_default.post_delay;
12934   titlemessage_first_default.auto_delay = title_first_default.auto_delay;
12935   titlemessage_first_default.auto_delay_unit =
12936     title_first_default.auto_delay_unit;
12937
12938   titlescreen_initial_default.fade_mode  = title_initial_default.fade_mode;
12939   titlescreen_initial_default.fade_delay = title_initial_default.fade_delay;
12940   titlescreen_initial_default.post_delay = title_initial_default.post_delay;
12941   titlescreen_initial_default.auto_delay = title_initial_default.auto_delay;
12942   titlescreen_initial_default.auto_delay_unit =
12943     title_initial_default.auto_delay_unit;
12944   titlescreen_default.fade_mode  = title_default.fade_mode;
12945   titlescreen_default.fade_delay = title_default.fade_delay;
12946   titlescreen_default.post_delay = title_default.post_delay;
12947   titlescreen_default.auto_delay = title_default.auto_delay;
12948   titlescreen_default.auto_delay_unit = title_default.auto_delay_unit;
12949   titlemessage_initial_default.fade_mode  = title_initial_default.fade_mode;
12950   titlemessage_initial_default.fade_delay = title_initial_default.fade_delay;
12951   titlemessage_initial_default.post_delay = title_initial_default.post_delay;
12952   titlemessage_initial_default.auto_delay_unit =
12953     title_initial_default.auto_delay_unit;
12954   titlemessage_default.fade_mode  = title_default.fade_mode;
12955   titlemessage_default.fade_delay = title_default.fade_delay;
12956   titlemessage_default.post_delay = title_default.post_delay;
12957   titlemessage_default.auto_delay = title_default.auto_delay;
12958   titlemessage_default.auto_delay_unit = title_default.auto_delay_unit;
12959
12960   // special case: initialize "ARG_DEFAULT" values in static default config
12961   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
12962   for (i = 0; i < MAX_NUM_TITLE_MESSAGES; i++)
12963   {
12964     titlescreen_initial_first[i] = titlescreen_initial_first_default;
12965     titlescreen_first[i] = titlescreen_first_default;
12966     titlemessage_initial_first[i] = titlemessage_initial_first_default;
12967     titlemessage_first[i] = titlemessage_first_default;
12968
12969     titlescreen_initial[i] = titlescreen_initial_default;
12970     titlescreen[i] = titlescreen_default;
12971     titlemessage_initial[i] = titlemessage_initial_default;
12972     titlemessage[i] = titlemessage_default;
12973   }
12974
12975   // special case: initialize "ARG_DEFAULT" values in static default config
12976   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
12977   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12978   {
12979     if (i == GFX_SPECIAL_ARG_TITLE)     // title values already initialized
12980       continue;
12981
12982     menu.enter_screen[i] = menu.enter_screen[GFX_SPECIAL_ARG_DEFAULT];
12983     menu.leave_screen[i] = menu.leave_screen[GFX_SPECIAL_ARG_DEFAULT];
12984     menu.next_screen[i]  = menu.next_screen[GFX_SPECIAL_ARG_DEFAULT];
12985   }
12986
12987   // special case: initialize "ARG_DEFAULT" values in static default config
12988   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
12989   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
12990   {
12991     viewport.window[i]    = viewport.window[GFX_SPECIAL_ARG_DEFAULT];
12992     viewport.playfield[i] = viewport.playfield[GFX_SPECIAL_ARG_DEFAULT];
12993     viewport.door_1[i]    = viewport.door_1[GFX_SPECIAL_ARG_DEFAULT];
12994
12995     if (i == GFX_SPECIAL_ARG_EDITOR)    // editor values already initialized
12996       continue;
12997
12998     viewport.door_2[i] = viewport.door_2[GFX_SPECIAL_ARG_DEFAULT];
12999   }
13000 }
13001
13002 static void InitMenuDesignSettings_SpecialPostProcessing(void)
13003 {
13004   static struct
13005   {
13006     struct XY *dst, *src;
13007   }
13008   game_buttons_xy[] =
13009   {
13010     { &game.button.save,        &game.button.stop       },
13011     { &game.button.pause2,      &game.button.pause      },
13012     { &game.button.load,        &game.button.play       },
13013     { &game.button.undo,        &game.button.stop       },
13014     { &game.button.redo,        &game.button.play       },
13015
13016     { NULL,                     NULL                    }
13017   };
13018   int i, j;
13019
13020   // special case: initialize later added SETUP list size from LEVELS value
13021   if (menu.list_size[GAME_MODE_SETUP] == -1)
13022     menu.list_size[GAME_MODE_SETUP] = menu.list_size[GAME_MODE_LEVELS];
13023
13024   // set default position for snapshot buttons to stop/pause/play buttons
13025   for (i = 0; game_buttons_xy[i].dst != NULL; i++)
13026     if ((*game_buttons_xy[i].dst).x == -1 &&
13027         (*game_buttons_xy[i].dst).y == -1)
13028       *game_buttons_xy[i].dst = *game_buttons_xy[i].src;
13029
13030   // --------------------------------------------------------------------------
13031   // dynamic viewports (including playfield margins, borders and alignments)
13032   // --------------------------------------------------------------------------
13033
13034   // dynamic viewports currently only supported for landscape mode
13035   int display_width  = MAX(video.display_width, video.display_height);
13036   int display_height = MIN(video.display_width, video.display_height);
13037
13038   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13039   {
13040     struct RectWithBorder *vp_window    = &viewport.window[i];
13041     struct RectWithBorder *vp_playfield = &viewport.playfield[i];
13042     struct RectWithBorder *vp_door_1    = &viewport.door_1[i];
13043     struct RectWithBorder *vp_door_2    = &viewport.door_2[i];
13044     boolean dynamic_window_width     = (vp_window->min_width     != -1);
13045     boolean dynamic_window_height    = (vp_window->min_height    != -1);
13046     boolean dynamic_playfield_width  = (vp_playfield->min_width  != -1);
13047     boolean dynamic_playfield_height = (vp_playfield->min_height != -1);
13048
13049     // adjust window size if min/max width/height is specified
13050
13051     if (vp_window->min_width != -1)
13052     {
13053       int window_width = display_width;
13054
13055       // when using static window height, use aspect ratio of display
13056       if (vp_window->min_height == -1)
13057         window_width = vp_window->height * display_width / display_height;
13058
13059       vp_window->width = MAX(vp_window->min_width, window_width);
13060     }
13061
13062     if (vp_window->min_height != -1)
13063     {
13064       int window_height = display_height;
13065
13066       // when using static window width, use aspect ratio of display
13067       if (vp_window->min_width == -1)
13068         window_height = vp_window->width * display_height / display_width;
13069
13070       vp_window->height = MAX(vp_window->min_height, window_height);
13071     }
13072
13073     if (vp_window->max_width != -1)
13074       vp_window->width = MIN(vp_window->width, vp_window->max_width);
13075
13076     if (vp_window->max_height != -1)
13077       vp_window->height = MIN(vp_window->height, vp_window->max_height);
13078
13079     int playfield_width  = vp_window->width;
13080     int playfield_height = vp_window->height;
13081
13082     // adjust playfield size and position according to specified margins
13083
13084     playfield_width  -= vp_playfield->margin_left;
13085     playfield_width  -= vp_playfield->margin_right;
13086
13087     playfield_height -= vp_playfield->margin_top;
13088     playfield_height -= vp_playfield->margin_bottom;
13089
13090     // adjust playfield size if min/max width/height is specified
13091
13092     if (vp_playfield->min_width != -1)
13093       vp_playfield->width = MAX(vp_playfield->min_width, playfield_width);
13094
13095     if (vp_playfield->min_height != -1)
13096       vp_playfield->height = MAX(vp_playfield->min_height, playfield_height);
13097
13098     if (vp_playfield->max_width != -1)
13099       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
13100
13101     if (vp_playfield->max_height != -1)
13102       vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
13103
13104     // adjust playfield position according to specified alignment
13105
13106     if (vp_playfield->align == ALIGN_LEFT || vp_playfield->x > 0)
13107       vp_playfield->x = ALIGNED_VP_XPOS(vp_playfield);
13108     else if (vp_playfield->align == ALIGN_CENTER)
13109       vp_playfield->x = playfield_width / 2 - vp_playfield->width / 2;
13110     else if (vp_playfield->align == ALIGN_RIGHT)
13111       vp_playfield->x += playfield_width - vp_playfield->width;
13112
13113     if (vp_playfield->valign == VALIGN_TOP || vp_playfield->y > 0)
13114       vp_playfield->y = ALIGNED_VP_YPOS(vp_playfield);
13115     else if (vp_playfield->valign == VALIGN_MIDDLE)
13116       vp_playfield->y = playfield_height / 2 - vp_playfield->height / 2;
13117     else if (vp_playfield->valign == VALIGN_BOTTOM)
13118       vp_playfield->y += playfield_height - vp_playfield->height;
13119
13120     vp_playfield->x += vp_playfield->margin_left;
13121     vp_playfield->y += vp_playfield->margin_top;
13122
13123     // adjust individual playfield borders if only default border is specified
13124
13125     if (vp_playfield->border_left == -1)
13126       vp_playfield->border_left = vp_playfield->border_size;
13127     if (vp_playfield->border_right == -1)
13128       vp_playfield->border_right = vp_playfield->border_size;
13129     if (vp_playfield->border_top == -1)
13130       vp_playfield->border_top = vp_playfield->border_size;
13131     if (vp_playfield->border_bottom == -1)
13132       vp_playfield->border_bottom = vp_playfield->border_size;
13133
13134     // set dynamic playfield borders if borders are specified as undefined
13135     // (but only if window size was dynamic and playfield size was static)
13136
13137     if (dynamic_window_width && !dynamic_playfield_width)
13138     {
13139       if (vp_playfield->border_left == -1)
13140       {
13141         vp_playfield->border_left = (vp_playfield->x -
13142                                      vp_playfield->margin_left);
13143         vp_playfield->x     -= vp_playfield->border_left;
13144         vp_playfield->width += vp_playfield->border_left;
13145       }
13146
13147       if (vp_playfield->border_right == -1)
13148       {
13149         vp_playfield->border_right = (vp_window->width -
13150                                       vp_playfield->x -
13151                                       vp_playfield->width -
13152                                       vp_playfield->margin_right);
13153         vp_playfield->width += vp_playfield->border_right;
13154       }
13155     }
13156
13157     if (dynamic_window_height && !dynamic_playfield_height)
13158     {
13159       if (vp_playfield->border_top == -1)
13160       {
13161         vp_playfield->border_top = (vp_playfield->y -
13162                                     vp_playfield->margin_top);
13163         vp_playfield->y      -= vp_playfield->border_top;
13164         vp_playfield->height += vp_playfield->border_top;
13165       }
13166
13167       if (vp_playfield->border_bottom == -1)
13168       {
13169         vp_playfield->border_bottom = (vp_window->height -
13170                                        vp_playfield->y -
13171                                        vp_playfield->height -
13172                                        vp_playfield->margin_bottom);
13173         vp_playfield->height += vp_playfield->border_bottom;
13174       }
13175     }
13176
13177     // adjust playfield size to be a multiple of a defined alignment tile size
13178
13179     int align_size = vp_playfield->align_size;
13180     int playfield_xtiles = vp_playfield->width  / align_size;
13181     int playfield_ytiles = vp_playfield->height / align_size;
13182     int playfield_width_corrected  = playfield_xtiles * align_size;
13183     int playfield_height_corrected = playfield_ytiles * align_size;
13184     boolean is_playfield_mode = (i == GFX_SPECIAL_ARG_PLAYING ||
13185                                  i == GFX_SPECIAL_ARG_EDITOR);
13186
13187     if (is_playfield_mode &&
13188         dynamic_playfield_width &&
13189         vp_playfield->width != playfield_width_corrected)
13190     {
13191       int playfield_xdiff = vp_playfield->width - playfield_width_corrected;
13192
13193       vp_playfield->width = playfield_width_corrected;
13194
13195       if (vp_playfield->align == ALIGN_LEFT)
13196       {
13197         vp_playfield->border_left += playfield_xdiff;
13198       }
13199       else if (vp_playfield->align == ALIGN_RIGHT)
13200       {
13201         vp_playfield->border_right += playfield_xdiff;
13202       }
13203       else if (vp_playfield->align == ALIGN_CENTER)
13204       {
13205         int border_left_diff  = playfield_xdiff / 2;
13206         int border_right_diff = playfield_xdiff - border_left_diff;
13207
13208         vp_playfield->border_left  += border_left_diff;
13209         vp_playfield->border_right += border_right_diff;
13210       }
13211     }
13212
13213     if (is_playfield_mode &&
13214         dynamic_playfield_height &&
13215         vp_playfield->height != playfield_height_corrected)
13216     {
13217       int playfield_ydiff = vp_playfield->height - playfield_height_corrected;
13218
13219       vp_playfield->height = playfield_height_corrected;
13220
13221       if (vp_playfield->valign == VALIGN_TOP)
13222       {
13223         vp_playfield->border_top += playfield_ydiff;
13224       }
13225       else if (vp_playfield->align == VALIGN_BOTTOM)
13226       {
13227         vp_playfield->border_right += playfield_ydiff;
13228       }
13229       else if (vp_playfield->align == VALIGN_MIDDLE)
13230       {
13231         int border_top_diff    = playfield_ydiff / 2;
13232         int border_bottom_diff = playfield_ydiff - border_top_diff;
13233
13234         vp_playfield->border_top    += border_top_diff;
13235         vp_playfield->border_bottom += border_bottom_diff;
13236       }
13237     }
13238
13239     // adjust door positions according to specified alignment
13240
13241     for (j = 0; j < 2; j++)
13242     {
13243       struct RectWithBorder *vp_door = (j == 0 ? vp_door_1 : vp_door_2);
13244
13245       if (vp_door->align == ALIGN_LEFT || vp_door->x > 0)
13246         vp_door->x = ALIGNED_VP_XPOS(vp_door);
13247       else if (vp_door->align == ALIGN_CENTER)
13248         vp_door->x = vp_window->width / 2 - vp_door->width / 2;
13249       else if (vp_door->align == ALIGN_RIGHT)
13250         vp_door->x += vp_window->width - vp_door->width;
13251
13252       if (vp_door->valign == VALIGN_TOP || vp_door->y > 0)
13253         vp_door->y = ALIGNED_VP_YPOS(vp_door);
13254       else if (vp_door->valign == VALIGN_MIDDLE)
13255         vp_door->y = vp_window->height / 2 - vp_door->height / 2;
13256       else if (vp_door->valign == VALIGN_BOTTOM)
13257         vp_door->y += vp_window->height - vp_door->height;
13258     }
13259   }
13260 }
13261
13262 static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
13263 {
13264   static struct
13265   {
13266     struct XYTileSize *dst, *src;
13267     int graphic;
13268   }
13269   editor_buttons_xy[] =
13270   {
13271     {
13272       &editor.button.element_left,      &editor.palette.element_left,
13273       IMG_GFX_EDITOR_BUTTON_ELEMENT_LEFT
13274     },
13275     {
13276       &editor.button.element_middle,    &editor.palette.element_middle,
13277       IMG_GFX_EDITOR_BUTTON_ELEMENT_MIDDLE
13278     },
13279     {
13280       &editor.button.element_right,     &editor.palette.element_right,
13281       IMG_GFX_EDITOR_BUTTON_ELEMENT_RIGHT
13282     },
13283
13284     { NULL,                     NULL                    }
13285   };
13286   int i;
13287
13288   // set default position for element buttons to element graphics
13289   for (i = 0; editor_buttons_xy[i].dst != NULL; i++)
13290   {
13291     if ((*editor_buttons_xy[i].dst).x == -1 &&
13292         (*editor_buttons_xy[i].dst).y == -1)
13293     {
13294       struct GraphicInfo *gd = &graphic_info[editor_buttons_xy[i].graphic];
13295
13296       gd->width = gd->height = editor_buttons_xy[i].src->tile_size;
13297
13298       *editor_buttons_xy[i].dst = *editor_buttons_xy[i].src;
13299     }
13300   }
13301
13302   // adjust editor palette rows and columns if specified to be dynamic
13303
13304   if (editor.palette.cols == -1)
13305   {
13306     int vp_width = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].width;
13307     int bt_width = graphic_info[IMG_EDITOR_PALETTE_BUTTON].width;
13308     int sc_width = graphic_info[IMG_EDITOR_PALETTE_SCROLLBAR].width;
13309
13310     editor.palette.cols = (vp_width - sc_width) / bt_width;
13311
13312     if (editor.palette.x == -1)
13313     {
13314       int palette_width = editor.palette.cols * bt_width + sc_width;
13315
13316       editor.palette.x = (vp_width - palette_width) / 2;
13317     }
13318   }
13319
13320   if (editor.palette.rows == -1)
13321   {
13322     int vp_height = viewport.playfield[GFX_SPECIAL_ARG_EDITOR].height;
13323     int bt_height = graphic_info[IMG_EDITOR_PALETTE_BUTTON].height;
13324     int tx_height = getFontHeight(FONT_TEXT_2);
13325
13326     editor.palette.rows = (vp_height - tx_height) / bt_height;
13327
13328     if (editor.palette.y == -1)
13329     {
13330       int palette_height = editor.palette.rows * bt_height + tx_height;
13331
13332       editor.palette.y = (vp_height - palette_height) / 2;
13333     }
13334   }
13335 }
13336
13337 static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
13338                                                       boolean initialize)
13339 {
13340   // special case: check if network and preview player positions are redefined,
13341   // to compare this later against the main menu level preview being redefined
13342   struct TokenIntPtrInfo menu_config_players[] =
13343   {
13344     { "main.network_players.x", &menu.main.network_players.redefined    },
13345     { "main.network_players.y", &menu.main.network_players.redefined    },
13346     { "main.preview_players.x", &menu.main.preview_players.redefined    },
13347     { "main.preview_players.y", &menu.main.preview_players.redefined    },
13348     { "preview.x",              &preview.redefined                      },
13349     { "preview.y",              &preview.redefined                      }
13350   };
13351   int i;
13352
13353   if (initialize)
13354   {
13355     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13356       *menu_config_players[i].value = FALSE;
13357   }
13358   else
13359   {
13360     for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
13361       if (getHashEntry(hash, menu_config_players[i].token) != NULL)
13362         *menu_config_players[i].value = TRUE;
13363   }
13364 }
13365
13366 static void InitMenuDesignSettings_PreviewPlayers(void)
13367 {
13368   InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
13369 }
13370
13371 static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
13372 {
13373   InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
13374 }
13375
13376 static void LoadMenuDesignSettingsFromFilename(char *filename)
13377 {
13378   static struct TitleFadingInfo tfi;
13379   static struct TitleMessageInfo tmi;
13380   static struct TokenInfo title_tokens[] =
13381   {
13382     { TYPE_INTEGER,     &tfi.fade_mode,         ".fade_mode"            },
13383     { TYPE_INTEGER,     &tfi.fade_delay,        ".fade_delay"           },
13384     { TYPE_INTEGER,     &tfi.post_delay,        ".post_delay"           },
13385     { TYPE_INTEGER,     &tfi.auto_delay,        ".auto_delay"           },
13386     { TYPE_INTEGER,     &tfi.auto_delay_unit,   ".auto_delay_unit"      },
13387
13388     { -1,               NULL,                   NULL                    }
13389   };
13390   static struct TokenInfo titlemessage_tokens[] =
13391   {
13392     { TYPE_INTEGER,     &tmi.x,                 ".x"                    },
13393     { TYPE_INTEGER,     &tmi.y,                 ".y"                    },
13394     { TYPE_INTEGER,     &tmi.width,             ".width"                },
13395     { TYPE_INTEGER,     &tmi.height,            ".height"               },
13396     { TYPE_INTEGER,     &tmi.chars,             ".chars"                },
13397     { TYPE_INTEGER,     &tmi.lines,             ".lines"                },
13398     { TYPE_INTEGER,     &tmi.align,             ".align"                },
13399     { TYPE_INTEGER,     &tmi.valign,            ".valign"               },
13400     { TYPE_INTEGER,     &tmi.font,              ".font"                 },
13401     { TYPE_BOOLEAN,     &tmi.autowrap,          ".autowrap"             },
13402     { TYPE_BOOLEAN,     &tmi.centered,          ".centered"             },
13403     { TYPE_BOOLEAN,     &tmi.parse_comments,    ".parse_comments"       },
13404     { TYPE_INTEGER,     &tmi.sort_priority,     ".sort_priority"        },
13405     { TYPE_INTEGER,     &tmi.fade_mode,         ".fade_mode"            },
13406     { TYPE_INTEGER,     &tmi.fade_delay,        ".fade_delay"           },
13407     { TYPE_INTEGER,     &tmi.post_delay,        ".post_delay"           },
13408     { TYPE_INTEGER,     &tmi.auto_delay,        ".auto_delay"           },
13409     { TYPE_INTEGER,     &tmi.auto_delay_unit,   ".auto_delay_unit"      },
13410
13411     { -1,               NULL,                   NULL                    }
13412   };
13413   static struct
13414   {
13415     struct TitleFadingInfo *info;
13416     char *text;
13417   }
13418   title_info[] =
13419   {
13420     // initialize first titles from "enter screen" definitions, if defined
13421     { &title_initial_first_default,     "menu.enter_screen.TITLE"       },
13422     { &title_first_default,             "menu.enter_screen.TITLE"       },
13423
13424     // initialize title screens from "next screen" definitions, if defined
13425     { &title_initial_default,           "menu.next_screen.TITLE"        },
13426     { &title_default,                   "menu.next_screen.TITLE"        },
13427
13428     { NULL,                             NULL                            }
13429   };
13430   static struct
13431   {
13432     struct TitleMessageInfo *array;
13433     char *text;
13434   }
13435   titlemessage_arrays[] =
13436   {
13437     // initialize first titles from "enter screen" definitions, if defined
13438     { titlescreen_initial_first,        "menu.enter_screen.TITLE"       },
13439     { titlescreen_first,                "menu.enter_screen.TITLE"       },
13440     { titlemessage_initial_first,       "menu.enter_screen.TITLE"       },
13441     { titlemessage_first,               "menu.enter_screen.TITLE"       },
13442
13443     // initialize titles from "next screen" definitions, if defined
13444     { titlescreen_initial,              "menu.next_screen.TITLE"        },
13445     { titlescreen,                      "menu.next_screen.TITLE"        },
13446     { titlemessage_initial,             "menu.next_screen.TITLE"        },
13447     { titlemessage,                     "menu.next_screen.TITLE"        },
13448
13449     // overwrite titles with title definitions, if defined
13450     { titlescreen_initial_first,        "[title_initial]"               },
13451     { titlescreen_first,                "[title]"                       },
13452     { titlemessage_initial_first,       "[title_initial]"               },
13453     { titlemessage_first,               "[title]"                       },
13454
13455     { titlescreen_initial,              "[title_initial]"               },
13456     { titlescreen,                      "[title]"                       },
13457     { titlemessage_initial,             "[title_initial]"               },
13458     { titlemessage,                     "[title]"                       },
13459
13460     // overwrite titles with title screen/message definitions, if defined
13461     { titlescreen_initial_first,        "[titlescreen_initial]"         },
13462     { titlescreen_first,                "[titlescreen]"                 },
13463     { titlemessage_initial_first,       "[titlemessage_initial]"        },
13464     { titlemessage_first,               "[titlemessage]"                },
13465
13466     { titlescreen_initial,              "[titlescreen_initial]"         },
13467     { titlescreen,                      "[titlescreen]"                 },
13468     { titlemessage_initial,             "[titlemessage_initial]"        },
13469     { titlemessage,                     "[titlemessage]"                },
13470
13471     { NULL,                             NULL                            }
13472   };
13473   SetupFileHash *setup_file_hash;
13474   int i, j, k;
13475
13476   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13477     return;
13478
13479   // the following initializes hierarchical values from dynamic configuration
13480
13481   // special case: initialize with default values that may be overwritten
13482   // (e.g., init "menu.draw_xoffset.INFO" from "menu.draw_xoffset")
13483   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13484   {
13485     struct TokenIntPtrInfo menu_config[] =
13486     {
13487       { "menu.draw_xoffset",    &menu.draw_xoffset[i]   },
13488       { "menu.draw_yoffset",    &menu.draw_yoffset[i]   },
13489       { "menu.list_size",       &menu.list_size[i]      }
13490     };
13491
13492     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13493     {
13494       char *token = menu_config[j].token;
13495       char *value = getHashEntry(setup_file_hash, token);
13496
13497       if (value != NULL)
13498         *menu_config[j].value = get_integer_from_string(value);
13499     }
13500   }
13501
13502   // special case: initialize with default values that may be overwritten
13503   // (eg, init "menu.draw_xoffset.INFO[XXX]" from "menu.draw_xoffset.INFO")
13504   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13505   {
13506     struct TokenIntPtrInfo menu_config[] =
13507     {
13508       { "menu.draw_xoffset.INFO",       &menu.draw_xoffset_info[i]      },
13509       { "menu.draw_yoffset.INFO",       &menu.draw_yoffset_info[i]      },
13510       { "menu.list_size.INFO",          &menu.list_size_info[i]         },
13511       { "menu.list_entry_size.INFO",    &menu.list_entry_size_info[i]   },
13512       { "menu.tile_size.INFO",          &menu.tile_size_info[i]         }
13513     };
13514
13515     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13516     {
13517       char *token = menu_config[j].token;
13518       char *value = getHashEntry(setup_file_hash, token);
13519
13520       if (value != NULL)
13521         *menu_config[j].value = get_integer_from_string(value);
13522     }
13523   }
13524
13525   // special case: initialize with default values that may be overwritten
13526   // (eg, init "menu.draw_xoffset.SETUP[XXX]" from "menu.draw_xoffset.SETUP")
13527   for (i = 0; i < NUM_SPECIAL_GFX_SETUP_ARGS; i++)
13528   {
13529     struct TokenIntPtrInfo menu_config[] =
13530     {
13531       { "menu.draw_xoffset.SETUP",      &menu.draw_xoffset_setup[i]     },
13532       { "menu.draw_yoffset.SETUP",      &menu.draw_yoffset_setup[i]     }
13533     };
13534
13535     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13536     {
13537       char *token = menu_config[j].token;
13538       char *value = getHashEntry(setup_file_hash, token);
13539
13540       if (value != NULL)
13541         *menu_config[j].value = get_integer_from_string(value);
13542     }
13543   }
13544
13545   // special case: initialize with default values that may be overwritten
13546   // (eg, init "menu.line_spacing.INFO[XXX]" from "menu.line_spacing.INFO")
13547   for (i = 0; i < NUM_SPECIAL_GFX_INFO_ARGS; i++)
13548   {
13549     struct TokenIntPtrInfo menu_config[] =
13550     {
13551       { "menu.left_spacing.INFO",       &menu.left_spacing_info[i]      },
13552       { "menu.middle_spacing.INFO",     &menu.middle_spacing_info[i]    },
13553       { "menu.right_spacing.INFO",      &menu.right_spacing_info[i]     },
13554       { "menu.top_spacing.INFO",        &menu.top_spacing_info[i]       },
13555       { "menu.bottom_spacing.INFO",     &menu.bottom_spacing_info[i]    },
13556       { "menu.paragraph_spacing.INFO",  &menu.paragraph_spacing_info[i] },
13557       { "menu.headline1_spacing.INFO",  &menu.headline1_spacing_info[i] },
13558       { "menu.headline2_spacing.INFO",  &menu.headline2_spacing_info[i] },
13559       { "menu.line_spacing.INFO",       &menu.line_spacing_info[i]      },
13560       { "menu.extra_spacing.INFO",      &menu.extra_spacing_info[i]     },
13561     };
13562
13563     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13564     {
13565       char *token = menu_config[j].token;
13566       char *value = getHashEntry(setup_file_hash, token);
13567
13568       if (value != NULL)
13569         *menu_config[j].value = get_integer_from_string(value);
13570     }
13571   }
13572
13573   // special case: initialize with default values that may be overwritten
13574   // (eg, init "menu.enter_screen.SCORES.xyz" from "menu.enter_screen.xyz")
13575   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13576   {
13577     struct TokenIntPtrInfo menu_config[] =
13578     {
13579       { "menu.enter_screen.fade_mode",  &menu.enter_screen[i].fade_mode  },
13580       { "menu.enter_screen.fade_delay", &menu.enter_screen[i].fade_delay },
13581       { "menu.enter_screen.post_delay", &menu.enter_screen[i].post_delay },
13582       { "menu.leave_screen.fade_mode",  &menu.leave_screen[i].fade_mode  },
13583       { "menu.leave_screen.fade_delay", &menu.leave_screen[i].fade_delay },
13584       { "menu.leave_screen.post_delay", &menu.leave_screen[i].post_delay },
13585       { "menu.next_screen.fade_mode",   &menu.next_screen[i].fade_mode   },
13586       { "menu.next_screen.fade_delay",  &menu.next_screen[i].fade_delay  },
13587       { "menu.next_screen.post_delay",  &menu.next_screen[i].post_delay  }
13588     };
13589
13590     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
13591     {
13592       char *token = menu_config[j].token;
13593       char *value = getHashEntry(setup_file_hash, token);
13594
13595       if (value != NULL)
13596         *menu_config[j].value = get_token_parameter_value(token, value);
13597     }
13598   }
13599
13600   // special case: initialize with default values that may be overwritten
13601   // (eg, init "viewport.door_1.MAIN.xyz" from "viewport.door_1.xyz")
13602   for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
13603   {
13604     struct
13605     {
13606       char *token_prefix;
13607       struct RectWithBorder *struct_ptr;
13608     }
13609     vp_struct[] =
13610     {
13611       { "viewport.window",      &viewport.window[i]     },
13612       { "viewport.playfield",   &viewport.playfield[i]  },
13613       { "viewport.door_1",      &viewport.door_1[i]     },
13614       { "viewport.door_2",      &viewport.door_2[i]     }
13615     };
13616
13617     for (j = 0; j < ARRAY_SIZE(vp_struct); j++)
13618     {
13619       struct TokenIntPtrInfo vp_config[] =
13620       {
13621         { ".x",                 &vp_struct[j].struct_ptr->x             },
13622         { ".y",                 &vp_struct[j].struct_ptr->y             },
13623         { ".width",             &vp_struct[j].struct_ptr->width         },
13624         { ".height",            &vp_struct[j].struct_ptr->height        },
13625         { ".min_width",         &vp_struct[j].struct_ptr->min_width     },
13626         { ".min_height",        &vp_struct[j].struct_ptr->min_height    },
13627         { ".max_width",         &vp_struct[j].struct_ptr->max_width     },
13628         { ".max_height",        &vp_struct[j].struct_ptr->max_height    },
13629         { ".margin_left",       &vp_struct[j].struct_ptr->margin_left   },
13630         { ".margin_right",      &vp_struct[j].struct_ptr->margin_right  },
13631         { ".margin_top",        &vp_struct[j].struct_ptr->margin_top    },
13632         { ".margin_bottom",     &vp_struct[j].struct_ptr->margin_bottom },
13633         { ".border_left",       &vp_struct[j].struct_ptr->border_left   },
13634         { ".border_right",      &vp_struct[j].struct_ptr->border_right  },
13635         { ".border_top",        &vp_struct[j].struct_ptr->border_top    },
13636         { ".border_bottom",     &vp_struct[j].struct_ptr->border_bottom },
13637         { ".border_size",       &vp_struct[j].struct_ptr->border_size   },
13638         { ".align_size",        &vp_struct[j].struct_ptr->align_size    },
13639         { ".align",             &vp_struct[j].struct_ptr->align         },
13640         { ".valign",            &vp_struct[j].struct_ptr->valign        }
13641       };
13642
13643       for (k = 0; k < ARRAY_SIZE(vp_config); k++)
13644       {
13645         char *token = getStringCat2(vp_struct[j].token_prefix,
13646                                     vp_config[k].token);
13647         char *value = getHashEntry(setup_file_hash, token);
13648
13649         if (value != NULL)
13650           *vp_config[k].value = get_token_parameter_value(token, value);
13651
13652         free(token);
13653       }
13654     }
13655   }
13656
13657   // special case: initialize with default values that may be overwritten
13658   // (e.g., init "[title].fade_mode" from "menu.next_screen.TITLE.fade_mode")
13659   for (i = 0; title_info[i].info != NULL; i++)
13660   {
13661     struct TitleFadingInfo *info = title_info[i].info;
13662     char *base_token = title_info[i].text;
13663
13664     for (j = 0; title_tokens[j].type != -1; j++)
13665     {
13666       char *token = getStringCat2(base_token, title_tokens[j].text);
13667       char *value = getHashEntry(setup_file_hash, token);
13668
13669       if (value != NULL)
13670       {
13671         int parameter_value = get_token_parameter_value(token, value);
13672
13673         tfi = *info;
13674
13675         *(int *)title_tokens[j].value = (int)parameter_value;
13676
13677         *info = tfi;
13678       }
13679
13680       free(token);
13681     }
13682   }
13683
13684   // special case: initialize with default values that may be overwritten
13685   // (e.g., init "titlemessage_1.fade_mode" from "[titlemessage].fade_mode")
13686   for (i = 0; titlemessage_arrays[i].array != NULL; i++)
13687   {
13688     struct TitleMessageInfo *array = titlemessage_arrays[i].array;
13689     char *base_token = titlemessage_arrays[i].text;
13690
13691     for (j = 0; titlemessage_tokens[j].type != -1; j++)
13692     {
13693       char *token = getStringCat2(base_token, titlemessage_tokens[j].text);
13694       char *value = getHashEntry(setup_file_hash, token);
13695
13696       if (value != NULL)
13697       {
13698         int parameter_value = get_token_parameter_value(token, value);
13699
13700         for (k = 0; k < MAX_NUM_TITLE_MESSAGES; k++)
13701         {
13702           tmi = array[k];
13703
13704           if (titlemessage_tokens[j].type == TYPE_INTEGER)
13705             *(int     *)titlemessage_tokens[j].value = (int)parameter_value;
13706           else
13707             *(boolean *)titlemessage_tokens[j].value = (boolean)parameter_value;
13708
13709           array[k] = tmi;
13710         }
13711       }
13712
13713       free(token);
13714     }
13715   }
13716
13717   // read (and overwrite with) values that may be specified in config file
13718   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
13719
13720   // special case: check if network and preview player positions are redefined
13721   InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
13722
13723   freeSetupFileHash(setup_file_hash);
13724 }
13725
13726 void LoadMenuDesignSettings(void)
13727 {
13728   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13729
13730   InitMenuDesignSettings_Static();
13731   InitMenuDesignSettings_SpecialPreProcessing();
13732   InitMenuDesignSettings_PreviewPlayers();
13733
13734   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
13735   {
13736     // first look for special settings configured in level series config
13737     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_GRAPHICS);
13738
13739     if (fileExists(filename_base))
13740       LoadMenuDesignSettingsFromFilename(filename_base);
13741   }
13742
13743   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_GRAPHICS);
13744
13745   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13746     LoadMenuDesignSettingsFromFilename(filename_local);
13747
13748   InitMenuDesignSettings_SpecialPostProcessing();
13749 }
13750
13751 void LoadMenuDesignSettings_AfterGraphics(void)
13752 {
13753   InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics();
13754 }
13755
13756 void InitSoundSettings_FromHash(SetupFileHash *setup_file_hash,
13757                                 boolean ignore_defaults)
13758 {
13759   int i;
13760
13761   for (i = 0; sound_config_vars[i].token != NULL; i++)
13762   {
13763     char *value = getHashEntry(setup_file_hash, sound_config_vars[i].token);
13764
13765     // (ignore definitions set to "[DEFAULT]" which are already initialized)
13766     if (ignore_defaults && strEqual(value, ARG_DEFAULT))
13767       continue;
13768
13769     if (value != NULL)
13770       *sound_config_vars[i].value =
13771         get_token_parameter_value(sound_config_vars[i].token, value);
13772   }
13773 }
13774
13775 void InitSoundSettings_Static(void)
13776 {
13777   // always start with reliable default values from static default config
13778   InitSoundSettings_FromHash(sound_config_hash, FALSE);
13779 }
13780
13781 static void LoadSoundSettingsFromFilename(char *filename)
13782 {
13783   SetupFileHash *setup_file_hash;
13784
13785   if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
13786     return;
13787
13788   // read (and overwrite with) values that may be specified in config file
13789   InitSoundSettings_FromHash(setup_file_hash, TRUE);
13790
13791   freeSetupFileHash(setup_file_hash);
13792 }
13793
13794 void LoadSoundSettings(void)
13795 {
13796   char *filename_base = UNDEFINED_FILENAME, *filename_local;
13797
13798   InitSoundSettings_Static();
13799
13800   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_SOUNDS))
13801   {
13802     // first look for special settings configured in level series config
13803     filename_base = getCustomArtworkLevelConfigFilename(ARTWORK_TYPE_SOUNDS);
13804
13805     if (fileExists(filename_base))
13806       LoadSoundSettingsFromFilename(filename_base);
13807   }
13808
13809   filename_local = getCustomArtworkConfigFilename(ARTWORK_TYPE_SOUNDS);
13810
13811   if (filename_local != NULL && !strEqual(filename_base, filename_local))
13812     LoadSoundSettingsFromFilename(filename_local);
13813 }
13814
13815 void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
13816 {
13817   char *filename = getEditorSetupFilename();
13818   SetupFileList *setup_file_list, *list;
13819   SetupFileHash *element_hash;
13820   int num_unknown_tokens = 0;
13821   int i;
13822
13823   if ((setup_file_list = loadSetupFileList(filename)) == NULL)
13824     return;
13825
13826   element_hash = newSetupFileHash();
13827
13828   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
13829     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
13830
13831   // determined size may be larger than needed (due to unknown elements)
13832   *num_elements = 0;
13833   for (list = setup_file_list; list != NULL; list = list->next)
13834     (*num_elements)++;
13835
13836   // add space for up to 3 more elements for padding that may be needed
13837   *num_elements += 3;
13838
13839   // free memory for old list of elements, if needed
13840   checked_free(*elements);
13841
13842   // allocate memory for new list of elements
13843   *elements = checked_malloc(*num_elements * sizeof(int));
13844
13845   *num_elements = 0;
13846   for (list = setup_file_list; list != NULL; list = list->next)
13847   {
13848     char *value = getHashEntry(element_hash, list->token);
13849
13850     if (value == NULL)          // try to find obsolete token mapping
13851     {
13852       char *mapped_token = get_mapped_token(list->token);
13853
13854       if (mapped_token != NULL)
13855       {
13856         value = getHashEntry(element_hash, mapped_token);
13857
13858         free(mapped_token);
13859       }
13860     }
13861
13862     if (value != NULL)
13863     {
13864       (*elements)[(*num_elements)++] = atoi(value);
13865     }
13866     else
13867     {
13868       if (num_unknown_tokens == 0)
13869       {
13870         Warn("---");
13871         Warn("unknown token(s) found in config file:");
13872         Warn("- config file: '%s'", filename);
13873
13874         num_unknown_tokens++;
13875       }
13876
13877       Warn("- token: '%s'", list->token);
13878     }
13879   }
13880
13881   if (num_unknown_tokens > 0)
13882     Warn("---");
13883
13884   while (*num_elements % 4)     // pad with empty elements, if needed
13885     (*elements)[(*num_elements)++] = EL_EMPTY;
13886
13887   freeSetupFileList(setup_file_list);
13888   freeSetupFileHash(element_hash);
13889
13890 #if 0
13891   for (i = 0; i < *num_elements; i++)
13892     Debug("editor", "element '%s' [%d]\n",
13893           element_info[(*elements)[i]].token_name, (*elements)[i]);
13894 #endif
13895 }
13896
13897 static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
13898                                                      boolean is_sound)
13899 {
13900   SetupFileHash *setup_file_hash = NULL;
13901   struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
13902   char *filename_music, *filename_prefix, *filename_info;
13903   struct
13904   {
13905     char *token;
13906     char **value_ptr;
13907   }
13908   token_to_value_ptr[] =
13909   {
13910     { "title_header",   &tmp_music_file_info.title_header       },
13911     { "artist_header",  &tmp_music_file_info.artist_header      },
13912     { "album_header",   &tmp_music_file_info.album_header       },
13913     { "year_header",    &tmp_music_file_info.year_header        },
13914     { "played_header",  &tmp_music_file_info.played_header      },
13915
13916     { "title",          &tmp_music_file_info.title              },
13917     { "artist",         &tmp_music_file_info.artist             },
13918     { "album",          &tmp_music_file_info.album              },
13919     { "year",           &tmp_music_file_info.year               },
13920     { "played",         &tmp_music_file_info.played             },
13921
13922     { NULL,             NULL                                    },
13923   };
13924   int i;
13925
13926   filename_music = (is_sound ? getCustomSoundFilename(basename) :
13927                     getCustomMusicFilename(basename));
13928
13929   if (filename_music == NULL)
13930     return NULL;
13931
13932   // ---------- try to replace file extension ----------
13933
13934   filename_prefix = getStringCopy(filename_music);
13935   if (strrchr(filename_prefix, '.') != NULL)
13936     *strrchr(filename_prefix, '.') = '\0';
13937   filename_info = getStringCat2(filename_prefix, ".txt");
13938
13939   if (fileExists(filename_info))
13940     setup_file_hash = loadSetupFileHash(filename_info);
13941
13942   free(filename_prefix);
13943   free(filename_info);
13944
13945   if (setup_file_hash == NULL)
13946   {
13947     // ---------- try to add file extension ----------
13948
13949     filename_prefix = getStringCopy(filename_music);
13950     filename_info = getStringCat2(filename_prefix, ".txt");
13951
13952     if (fileExists(filename_info))
13953       setup_file_hash = loadSetupFileHash(filename_info);
13954
13955     free(filename_prefix);
13956     free(filename_info);
13957   }
13958
13959   if (setup_file_hash == NULL)
13960     return NULL;
13961
13962   // ---------- music file info found ----------
13963
13964   clear_mem(&tmp_music_file_info, sizeof(struct MusicFileInfo));
13965
13966   for (i = 0; token_to_value_ptr[i].token != NULL; i++)
13967   {
13968     char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
13969
13970     *token_to_value_ptr[i].value_ptr =
13971       getStringCopy(value != NULL && *value != '\0' ? value : UNKNOWN_NAME);
13972   }
13973
13974   tmp_music_file_info.basename = getStringCopy(basename);
13975   tmp_music_file_info.music = music;
13976   tmp_music_file_info.is_sound = is_sound;
13977
13978   new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
13979   *new_music_file_info = tmp_music_file_info;
13980
13981   return new_music_file_info;
13982 }
13983
13984 static struct MusicFileInfo *get_music_file_info(char *basename, int music)
13985 {
13986   return get_music_file_info_ext(basename, music, FALSE);
13987 }
13988
13989 static struct MusicFileInfo *get_sound_file_info(char *basename, int sound)
13990 {
13991   return get_music_file_info_ext(basename, sound, TRUE);
13992 }
13993
13994 static boolean music_info_listed_ext(struct MusicFileInfo *list,
13995                                      char *basename, boolean is_sound)
13996 {
13997   for (; list != NULL; list = list->next)
13998     if (list->is_sound == is_sound && strEqual(list->basename, basename))
13999       return TRUE;
14000
14001   return FALSE;
14002 }
14003
14004 static boolean music_info_listed(struct MusicFileInfo *list, char *basename)
14005 {
14006   return music_info_listed_ext(list, basename, FALSE);
14007 }
14008
14009 static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
14010 {
14011   return music_info_listed_ext(list, basename, TRUE);
14012 }
14013
14014 void LoadMusicInfo(void)
14015 {
14016   int num_music_noconf = getMusicListSize_NoConf();
14017   int num_music = getMusicListSize();
14018   int num_sounds = getSoundListSize();
14019   struct FileInfo *music, *sound;
14020   struct MusicFileInfo *next, **new;
14021
14022   int i;
14023
14024   while (music_file_info != NULL)
14025   {
14026     next = music_file_info->next;
14027
14028     checked_free(music_file_info->basename);
14029
14030     checked_free(music_file_info->title_header);
14031     checked_free(music_file_info->artist_header);
14032     checked_free(music_file_info->album_header);
14033     checked_free(music_file_info->year_header);
14034     checked_free(music_file_info->played_header);
14035
14036     checked_free(music_file_info->title);
14037     checked_free(music_file_info->artist);
14038     checked_free(music_file_info->album);
14039     checked_free(music_file_info->year);
14040     checked_free(music_file_info->played);
14041
14042     free(music_file_info);
14043
14044     music_file_info = next;
14045   }
14046
14047   new = &music_file_info;
14048
14049   // get (configured or unconfigured) music file info for all levels
14050   for (i = leveldir_current->first_level;
14051        i <= leveldir_current->last_level; i++)
14052   {
14053     int music_nr;
14054
14055     if (levelset.music[i] != MUS_UNDEFINED)
14056     {
14057       // get music file info for configured level music
14058       music_nr = levelset.music[i];
14059     }
14060     else if (num_music_noconf > 0)
14061     {
14062       // get music file info for unconfigured level music
14063       int level_pos = i - leveldir_current->first_level;
14064
14065       music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
14066     }
14067     else
14068     {
14069       continue;
14070     }
14071
14072     char *basename = getMusicInfoEntryFilename(music_nr);
14073
14074     if (basename == NULL)
14075       continue;
14076
14077     if (!music_info_listed(music_file_info, basename))
14078     {
14079       *new = get_music_file_info(basename, music_nr);
14080
14081       if (*new != NULL)
14082         new = &(*new)->next;
14083     }
14084   }
14085
14086   // get music file info for all remaining configured music files
14087   for (i = 0; i < num_music; i++)
14088   {
14089     music = getMusicListEntry(i);
14090
14091     if (music->filename == NULL)
14092       continue;
14093
14094     if (strEqual(music->filename, UNDEFINED_FILENAME))
14095       continue;
14096
14097     // a configured file may be not recognized as music
14098     if (!FileIsMusic(music->filename))
14099       continue;
14100
14101     if (!music_info_listed(music_file_info, music->filename))
14102     {
14103       *new = get_music_file_info(music->filename, i);
14104
14105       if (*new != NULL)
14106         new = &(*new)->next;
14107     }
14108   }
14109
14110   // get sound file info for all configured sound files
14111   for (i = 0; i < num_sounds; i++)
14112   {
14113     sound = getSoundListEntry(i);
14114
14115     if (sound->filename == NULL)
14116       continue;
14117
14118     if (strEqual(sound->filename, UNDEFINED_FILENAME))
14119       continue;
14120
14121     // a configured file may be not recognized as sound
14122     if (!FileIsSound(sound->filename))
14123       continue;
14124
14125     if (!sound_info_listed(music_file_info, sound->filename))
14126     {
14127       *new = get_sound_file_info(sound->filename, i);
14128       if (*new != NULL)
14129         new = &(*new)->next;
14130     }
14131   }
14132
14133   // add pointers to previous list nodes
14134
14135   struct MusicFileInfo *node = music_file_info;
14136
14137   while (node != NULL)
14138   {
14139     if (node->next)
14140       node->next->prev = node;
14141
14142     node = node->next;
14143   }
14144 }
14145
14146 static void add_helpanim_entry(int element, int action, int direction,
14147                                int delay, int *num_list_entries)
14148 {
14149   struct HelpAnimInfo *new_list_entry;
14150   (*num_list_entries)++;
14151
14152   helpanim_info =
14153     checked_realloc(helpanim_info,
14154                     *num_list_entries * sizeof(struct HelpAnimInfo));
14155   new_list_entry = &helpanim_info[*num_list_entries - 1];
14156
14157   new_list_entry->element = element;
14158   new_list_entry->action = action;
14159   new_list_entry->direction = direction;
14160   new_list_entry->delay = delay;
14161 }
14162
14163 static void print_unknown_token(char *filename, char *token, int token_nr)
14164 {
14165   if (token_nr == 0)
14166   {
14167     Warn("---");
14168     Warn("unknown token(s) found in config file:");
14169     Warn("- config file: '%s'", filename);
14170   }
14171
14172   Warn("- token: '%s'", token);
14173 }
14174
14175 static void print_unknown_token_end(int token_nr)
14176 {
14177   if (token_nr > 0)
14178     Warn("---");
14179 }
14180
14181 void LoadHelpAnimInfo(void)
14182 {
14183   char *filename = getHelpAnimFilename();
14184   SetupFileList *setup_file_list = NULL, *list;
14185   SetupFileHash *element_hash, *action_hash, *direction_hash;
14186   int num_list_entries = 0;
14187   int num_unknown_tokens = 0;
14188   int i;
14189
14190   if (fileExists(filename))
14191     setup_file_list = loadSetupFileList(filename);
14192
14193   if (setup_file_list == NULL)
14194   {
14195     // use reliable default values from static configuration
14196     SetupFileList *insert_ptr;
14197
14198     insert_ptr = setup_file_list =
14199       newSetupFileList(helpanim_config[0].token,
14200                        helpanim_config[0].value);
14201
14202     for (i = 1; helpanim_config[i].token; i++)
14203       insert_ptr = addListEntry(insert_ptr,
14204                                 helpanim_config[i].token,
14205                                 helpanim_config[i].value);
14206   }
14207
14208   element_hash   = newSetupFileHash();
14209   action_hash    = newSetupFileHash();
14210   direction_hash = newSetupFileHash();
14211
14212   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14213     setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
14214
14215   for (i = 0; i < NUM_ACTIONS; i++)
14216     setHashEntry(action_hash, element_action_info[i].suffix,
14217                  i_to_a(element_action_info[i].value));
14218
14219   // do not store direction index (bit) here, but direction value!
14220   for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
14221     setHashEntry(direction_hash, element_direction_info[i].suffix,
14222                  i_to_a(1 << element_direction_info[i].value));
14223
14224   for (list = setup_file_list; list != NULL; list = list->next)
14225   {
14226     char *element_token, *action_token, *direction_token;
14227     char *element_value, *action_value, *direction_value;
14228     int delay = atoi(list->value);
14229
14230     if (strEqual(list->token, "end"))
14231     {
14232       add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14233
14234       continue;
14235     }
14236
14237     /* first try to break element into element/action/direction parts;
14238        if this does not work, also accept combined "element[.act][.dir]"
14239        elements (like "dynamite.active"), which are unique elements */
14240
14241     if (strchr(list->token, '.') == NULL)       // token contains no '.'
14242     {
14243       element_value = getHashEntry(element_hash, list->token);
14244       if (element_value != NULL)        // element found
14245         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14246                            &num_list_entries);
14247       else
14248       {
14249         // no further suffixes found -- this is not an element
14250         print_unknown_token(filename, list->token, num_unknown_tokens++);
14251       }
14252
14253       continue;
14254     }
14255
14256     // token has format "<prefix>.<something>"
14257
14258     action_token = strchr(list->token, '.');    // suffix may be action ...
14259     direction_token = action_token;             // ... or direction
14260
14261     element_token = getStringCopy(list->token);
14262     *strchr(element_token, '.') = '\0';
14263
14264     element_value = getHashEntry(element_hash, element_token);
14265
14266     if (element_value == NULL)          // this is no element
14267     {
14268       element_value = getHashEntry(element_hash, list->token);
14269       if (element_value != NULL)        // combined element found
14270         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14271                            &num_list_entries);
14272       else
14273         print_unknown_token(filename, list->token, num_unknown_tokens++);
14274
14275       free(element_token);
14276
14277       continue;
14278     }
14279
14280     action_value = getHashEntry(action_hash, action_token);
14281
14282     if (action_value != NULL)           // action found
14283     {
14284       add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
14285                     &num_list_entries);
14286
14287       free(element_token);
14288
14289       continue;
14290     }
14291
14292     direction_value = getHashEntry(direction_hash, direction_token);
14293
14294     if (direction_value != NULL)        // direction found
14295     {
14296       add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
14297                          &num_list_entries);
14298
14299       free(element_token);
14300
14301       continue;
14302     }
14303
14304     if (strchr(action_token + 1, '.') == NULL)
14305     {
14306       // no further suffixes found -- this is not an action nor direction
14307
14308       element_value = getHashEntry(element_hash, list->token);
14309       if (element_value != NULL)        // combined element found
14310         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14311                            &num_list_entries);
14312       else
14313         print_unknown_token(filename, list->token, num_unknown_tokens++);
14314
14315       free(element_token);
14316
14317       continue;
14318     }
14319
14320     // token has format "<prefix>.<suffix>.<something>"
14321
14322     direction_token = strchr(action_token + 1, '.');
14323
14324     action_token = getStringCopy(action_token);
14325     *strchr(action_token + 1, '.') = '\0';
14326
14327     action_value = getHashEntry(action_hash, action_token);
14328
14329     if (action_value == NULL)           // this is no action
14330     {
14331       element_value = getHashEntry(element_hash, list->token);
14332       if (element_value != NULL)        // combined element found
14333         add_helpanim_entry(atoi(element_value), -1, -1, delay,
14334                            &num_list_entries);
14335       else
14336         print_unknown_token(filename, list->token, num_unknown_tokens++);
14337
14338       free(element_token);
14339       free(action_token);
14340
14341       continue;
14342     }
14343
14344     direction_value = getHashEntry(direction_hash, direction_token);
14345
14346     if (direction_value != NULL)        // direction found
14347     {
14348       add_helpanim_entry(atoi(element_value), atoi(action_value),
14349                          atoi(direction_value), delay, &num_list_entries);
14350
14351       free(element_token);
14352       free(action_token);
14353
14354       continue;
14355     }
14356
14357     // this is no direction
14358
14359     element_value = getHashEntry(element_hash, list->token);
14360     if (element_value != NULL)          // combined element found
14361       add_helpanim_entry(atoi(element_value), -1, -1, delay,
14362                          &num_list_entries);
14363     else
14364       print_unknown_token(filename, list->token, num_unknown_tokens++);
14365
14366     free(element_token);
14367     free(action_token);
14368   }
14369
14370   print_unknown_token_end(num_unknown_tokens);
14371
14372   add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
14373   add_helpanim_entry(HELPANIM_LIST_END,  -1, -1, -1, &num_list_entries);
14374
14375   freeSetupFileList(setup_file_list);
14376   freeSetupFileHash(element_hash);
14377   freeSetupFileHash(action_hash);
14378   freeSetupFileHash(direction_hash);
14379
14380 #if 0
14381   for (i = 0; i < num_list_entries; i++)
14382     Debug("files:LoadHelpAnimInfo", "'%s': %d, %d, %d => %d",
14383           EL_NAME(helpanim_info[i].element),
14384           helpanim_info[i].element,
14385           helpanim_info[i].action,
14386           helpanim_info[i].direction,
14387           helpanim_info[i].delay);
14388 #endif
14389 }
14390
14391 void LoadHelpTextInfo(void)
14392 {
14393   char *filename = getHelpTextFilename();
14394   int i;
14395
14396   if (helptext_info != NULL)
14397   {
14398     freeSetupFileHash(helptext_info);
14399     helptext_info = NULL;
14400   }
14401
14402   if (fileExists(filename))
14403     helptext_info = loadSetupFileHash(filename);
14404
14405   if (helptext_info == NULL)
14406   {
14407     // use reliable default values from static configuration
14408     helptext_info = newSetupFileHash();
14409
14410     for (i = 0; helptext_config[i].token; i++)
14411       setHashEntry(helptext_info,
14412                    helptext_config[i].token,
14413                    helptext_config[i].value);
14414   }
14415
14416 #if 0
14417   BEGIN_HASH_ITERATION(helptext_info, itr)
14418   {
14419     Debug("files:LoadHelpTextInfo", "'%s' => '%s'",
14420           HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
14421   }
14422   END_HASH_ITERATION(hash, itr)
14423 #endif
14424 }
14425
14426
14427 // ----------------------------------------------------------------------------
14428 // convert levels
14429 // ----------------------------------------------------------------------------
14430
14431 #define MAX_NUM_CONVERT_LEVELS          1000
14432
14433 void ConvertLevels(void)
14434 {
14435   static LevelDirTree *convert_leveldir = NULL;
14436   static int convert_level_nr = -1;
14437   static int num_levels_handled = 0;
14438   static int num_levels_converted = 0;
14439   static boolean levels_failed[MAX_NUM_CONVERT_LEVELS];
14440   int i;
14441
14442   convert_leveldir = getTreeInfoFromIdentifier(leveldir_first,
14443                                                global.convert_leveldir);
14444
14445   if (convert_leveldir == NULL)
14446     Fail("no such level identifier: '%s'", global.convert_leveldir);
14447
14448   leveldir_current = convert_leveldir;
14449
14450   if (global.convert_level_nr != -1)
14451   {
14452     convert_leveldir->first_level = global.convert_level_nr;
14453     convert_leveldir->last_level  = global.convert_level_nr;
14454   }
14455
14456   convert_level_nr = convert_leveldir->first_level;
14457
14458   PrintLine("=", 79);
14459   Print("Converting levels\n");
14460   PrintLine("-", 79);
14461   Print("Level series identifier: '%s'\n", convert_leveldir->identifier);
14462   Print("Level series name:       '%s'\n", convert_leveldir->name);
14463   Print("Level series author:     '%s'\n", convert_leveldir->author);
14464   Print("Number of levels:        %d\n",   convert_leveldir->levels);
14465   PrintLine("=", 79);
14466   Print("\n");
14467
14468   for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14469     levels_failed[i] = FALSE;
14470
14471   while (convert_level_nr <= convert_leveldir->last_level)
14472   {
14473     char *level_filename;
14474     boolean new_level;
14475
14476     level_nr = convert_level_nr++;
14477
14478     Print("Level %03d: ", level_nr);
14479
14480     LoadLevel(level_nr);
14481     if (level.no_level_file || level.no_valid_file)
14482     {
14483       Print("(no level)\n");
14484       continue;
14485     }
14486
14487     Print("converting level ... ");
14488
14489 #if 0
14490     // special case: conversion of some EMC levels as requested by ACME
14491     level.game_engine_type = GAME_ENGINE_TYPE_RND;
14492 #endif
14493
14494     level_filename = getDefaultLevelFilename(level_nr);
14495     new_level = !fileExists(level_filename);
14496
14497     if (new_level)
14498     {
14499       SaveLevel(level_nr);
14500
14501       num_levels_converted++;
14502
14503       Print("converted.\n");
14504     }
14505     else
14506     {
14507       if (level_nr >= 0 && level_nr < MAX_NUM_CONVERT_LEVELS)
14508         levels_failed[level_nr] = TRUE;
14509
14510       Print("NOT CONVERTED -- LEVEL ALREADY EXISTS.\n");
14511     }
14512
14513     num_levels_handled++;
14514   }
14515
14516   Print("\n");
14517   PrintLine("=", 79);
14518   Print("Number of levels handled: %d\n", num_levels_handled);
14519   Print("Number of levels converted: %d (%d%%)\n", num_levels_converted,
14520          (num_levels_handled ?
14521           num_levels_converted * 100 / num_levels_handled : 0));
14522   PrintLine("-", 79);
14523   Print("Summary (for automatic parsing by scripts):\n");
14524   Print("LEVELDIR '%s', CONVERTED %d/%d (%d%%)",
14525          convert_leveldir->identifier, num_levels_converted,
14526          num_levels_handled,
14527          (num_levels_handled ?
14528           num_levels_converted * 100 / num_levels_handled : 0));
14529
14530   if (num_levels_handled != num_levels_converted)
14531   {
14532     Print(", FAILED:");
14533     for (i = 0; i < MAX_NUM_CONVERT_LEVELS; i++)
14534       if (levels_failed[i])
14535         Print(" %03d", i);
14536   }
14537
14538   Print("\n");
14539   PrintLine("=", 79);
14540
14541   CloseAllAndExit(0);
14542 }
14543
14544
14545 // ----------------------------------------------------------------------------
14546 // create and save images for use in level sketches (raw BMP format)
14547 // ----------------------------------------------------------------------------
14548
14549 void CreateLevelSketchImages(void)
14550 {
14551   Bitmap *bitmap1;
14552   Bitmap *bitmap2;
14553   int i;
14554
14555   InitElementPropertiesGfxElement();
14556
14557   bitmap1 = CreateBitmap(TILEX, TILEY, DEFAULT_DEPTH);
14558   bitmap2 = CreateBitmap(MINI_TILEX, MINI_TILEY, DEFAULT_DEPTH);
14559
14560   for (i = 0; i < NUM_FILE_ELEMENTS; i++)
14561   {
14562     int element = getMappedElement(i);
14563     char basename1[16];
14564     char basename2[16];
14565     char *filename1;
14566     char *filename2;
14567
14568     sprintf(basename1, "%04d.bmp", i);
14569     sprintf(basename2, "%04ds.bmp", i);
14570
14571     filename1 = getPath2(global.create_sketch_images_dir, basename1);
14572     filename2 = getPath2(global.create_sketch_images_dir, basename2);
14573
14574     DrawSizedElement(0, 0, element, TILESIZE);
14575     BlitBitmap(drawto, bitmap1, SX, SY, TILEX, TILEY, 0, 0);
14576
14577     if (SDL_SaveBMP(bitmap1->surface, filename1) != 0)
14578       Fail("cannot save level sketch image file '%s'", filename1);
14579
14580     DrawSizedElement(0, 0, element, MINI_TILESIZE);
14581     BlitBitmap(drawto, bitmap2, SX, SY, MINI_TILEX, MINI_TILEY, 0, 0);
14582
14583     if (SDL_SaveBMP(bitmap2->surface, filename2) != 0)
14584       Fail("cannot save level sketch image file '%s'", filename2);
14585
14586     free(filename1);
14587     free(filename2);
14588
14589     // create corresponding SQL statements (for normal and small images)
14590     if (i < 1000)
14591     {
14592       printf("insert into phpbb_words values (NULL, '`%03d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14593       printf("insert into phpbb_words values (NULL, '¸%03d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14594     }
14595
14596     printf("insert into phpbb_words values (NULL, '`%04d', '<IMG class=\"levelsketch\" src=\"/I/%04d.png\"/>');\n", i, i);
14597     printf("insert into phpbb_words values (NULL, '¸%04d', '<IMG class=\"levelsketch\" src=\"/I/%04ds.png\"/>');\n", i, i);
14598
14599     // optional: create content for forum level sketch demonstration post
14600     if (options.debug)
14601       fprintf(stderr, "%03d `%03d%c", i, i, (i % 10 < 9 ? ' ' : '\n'));
14602   }
14603
14604   FreeBitmap(bitmap1);
14605   FreeBitmap(bitmap2);
14606
14607   if (options.debug)
14608     fprintf(stderr, "\n");
14609
14610   Info("%d normal and small images created", NUM_FILE_ELEMENTS);
14611
14612   CloseAllAndExit(0);
14613 }
14614
14615
14616 // ----------------------------------------------------------------------------
14617 // create and save images for element collecting animations (raw BMP format)
14618 // ----------------------------------------------------------------------------
14619
14620 static boolean createCollectImage(int element)
14621 {
14622   return (IS_COLLECTIBLE(element) && !IS_SP_ELEMENT(element));
14623 }
14624
14625 void CreateCollectElementImages(void)
14626 {
14627   int i, j;
14628   int num_steps = 8;
14629   int anim_frames = num_steps - 1;
14630   int tile_size = TILESIZE;
14631   int anim_width  = tile_size * anim_frames;
14632   int anim_height = tile_size;
14633   int num_collect_images = 0;
14634   int pos_collect_images = 0;
14635
14636   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14637     if (createCollectImage(i))
14638       num_collect_images++;
14639
14640   Info("Creating %d element collecting animation images ...",
14641        num_collect_images);
14642
14643   int dst_width  = anim_width * 2;
14644   int dst_height = anim_height * num_collect_images / 2;
14645   Bitmap *dst_bitmap = CreateBitmap(dst_width, dst_height, DEFAULT_DEPTH);
14646   char *basename_bmp = "RocksCollect.bmp";
14647   char *basename_png = "RocksCollect.png";
14648   char *filename_bmp = getPath2(global.create_collect_images_dir, basename_bmp);
14649   char *filename_png = getPath2(global.create_collect_images_dir, basename_png);
14650   int len_filename_bmp = strlen(filename_bmp);
14651   int len_filename_png = strlen(filename_png);
14652   int max_command_len = MAX_FILENAME_LEN + len_filename_bmp + len_filename_png;
14653   char cmd_convert[max_command_len];
14654
14655   snprintf(cmd_convert, max_command_len, "convert \"%s\" \"%s\"",
14656            filename_bmp,
14657            filename_png);
14658
14659   // force using RGBA surface for destination bitmap
14660   SDL_SetColorKey(dst_bitmap->surface, SET_TRANSPARENT_PIXEL,
14661                   SDL_MapRGB(dst_bitmap->surface->format, 0x00, 0x00, 0x00));
14662
14663   dst_bitmap->surface =
14664     SDL_ConvertSurfaceFormat(dst_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14665
14666   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
14667   {
14668     if (!createCollectImage(i))
14669       continue;
14670
14671     int dst_x = (pos_collect_images / (num_collect_images / 2)) * anim_width;
14672     int dst_y = (pos_collect_images % (num_collect_images / 2)) * anim_height;
14673     int graphic = el2img(i);
14674     char *token_name = element_info[i].token_name;
14675     Bitmap *tmp_bitmap = CreateBitmap(tile_size, tile_size, DEFAULT_DEPTH);
14676     Bitmap *src_bitmap;
14677     int src_x, src_y;
14678
14679     Info("- creating collecting image for '%s' ...", token_name);
14680
14681     getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
14682
14683     BlitBitmap(src_bitmap, tmp_bitmap, src_x, src_y,
14684                tile_size, tile_size, 0, 0);
14685
14686     // force using RGBA surface for temporary bitmap (using transparent black)
14687     SDL_SetColorKey(tmp_bitmap->surface, SET_TRANSPARENT_PIXEL,
14688                     SDL_MapRGB(tmp_bitmap->surface->format, 0x00, 0x00, 0x00));
14689
14690     tmp_bitmap->surface =
14691       SDL_ConvertSurfaceFormat(tmp_bitmap->surface, SDL_PIXELFORMAT_ARGB8888, 0);
14692
14693     tmp_bitmap->surface_masked = tmp_bitmap->surface;
14694
14695     for (j = 0; j < anim_frames; j++)
14696     {
14697       int frame_size_final = tile_size * (anim_frames - j) / num_steps;
14698       int frame_size = frame_size_final * num_steps;
14699       int offset = (tile_size - frame_size_final) / 2;
14700       Bitmap *frame_bitmap = ZoomBitmap(tmp_bitmap, frame_size, frame_size);
14701
14702       while (frame_size > frame_size_final)
14703       {
14704         frame_size /= 2;
14705
14706         Bitmap *half_bitmap = ZoomBitmap(frame_bitmap, frame_size, frame_size);
14707
14708         FreeBitmap(frame_bitmap);
14709
14710         frame_bitmap = half_bitmap;
14711       }
14712
14713       BlitBitmapMasked(frame_bitmap, dst_bitmap, 0, 0,
14714                        frame_size_final, frame_size_final,
14715                        dst_x + j * tile_size + offset, dst_y + offset);
14716
14717       FreeBitmap(frame_bitmap);
14718     }
14719
14720     tmp_bitmap->surface_masked = NULL;
14721
14722     FreeBitmap(tmp_bitmap);
14723
14724     pos_collect_images++;
14725   }
14726
14727   if (SDL_SaveBMP(dst_bitmap->surface, filename_bmp) != 0)
14728     Fail("cannot save element collecting image file '%s'", filename_bmp);
14729
14730   FreeBitmap(dst_bitmap);
14731
14732   Info("Converting image file from BMP to PNG ...");
14733
14734   if (system(cmd_convert) != 0)
14735     Fail("converting image file failed");
14736
14737   unlink(filename_bmp);
14738
14739   Info("Done.");
14740
14741   CloseAllAndExit(0);
14742 }
14743
14744
14745 // ----------------------------------------------------------------------------
14746 // create and save images for custom and group elements (raw BMP format)
14747 // ----------------------------------------------------------------------------
14748
14749 void CreateCustomElementImages(char *directory)
14750 {
14751   char *src_basename = "RocksCE-template.ilbm";
14752   char *dst_basename = "RocksCE.bmp";
14753   char *src_filename = getPath2(directory, src_basename);
14754   char *dst_filename = getPath2(directory, dst_basename);
14755   Bitmap *src_bitmap;
14756   Bitmap *bitmap;
14757   int yoffset_ce = 0;
14758   int yoffset_ge = (TILEY * NUM_CUSTOM_ELEMENTS / 16);
14759   int i;
14760
14761   InitVideoDefaults();
14762
14763   ReCreateBitmap(&backbuffer, video.width, video.height);
14764
14765   src_bitmap = LoadImage(src_filename);
14766
14767   bitmap = CreateBitmap(TILEX * 16 * 2,
14768                         TILEY * (NUM_CUSTOM_ELEMENTS + NUM_GROUP_ELEMENTS) / 16,
14769                         DEFAULT_DEPTH);
14770
14771   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14772   {
14773     int x = i % 16;
14774     int y = i / 16;
14775     int ii = i + 1;
14776     int j;
14777
14778     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14779                TILEX * x, TILEY * y + yoffset_ce);
14780
14781     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14782                TILEX, TILEY,
14783                TILEX * x + TILEX * 16,
14784                TILEY * y + yoffset_ce);
14785
14786     for (j = 2; j >= 0; j--)
14787     {
14788       int c = ii % 10;
14789
14790       BlitBitmap(src_bitmap, bitmap,
14791                  TILEX + c * 7, 0, 6, 10,
14792                  TILEX * x + 6 + j * 7,
14793                  TILEY * y + 11 + yoffset_ce);
14794
14795       BlitBitmap(src_bitmap, bitmap,
14796                  TILEX + c * 8, TILEY, 6, 10,
14797                  TILEX * 16 + TILEX * x + 6 + j * 8,
14798                  TILEY * y + 10 + yoffset_ce);
14799
14800       ii /= 10;
14801     }
14802   }
14803
14804   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14805   {
14806     int x = i % 16;
14807     int y = i / 16;
14808     int ii = i + 1;
14809     int j;
14810
14811     BlitBitmap(src_bitmap, bitmap, 0, 0, TILEX, TILEY,
14812                TILEX * x, TILEY * y + yoffset_ge);
14813
14814     BlitBitmap(src_bitmap, bitmap, 0, TILEY,
14815                TILEX, TILEY,
14816                TILEX * x + TILEX * 16,
14817                TILEY * y + yoffset_ge);
14818
14819     for (j = 1; j >= 0; j--)
14820     {
14821       int c = ii % 10;
14822
14823       BlitBitmap(src_bitmap, bitmap, TILEX + c * 10, 11, 10, 10,
14824                  TILEX * x + 6 + j * 10,
14825                  TILEY * y + 11 + yoffset_ge);
14826
14827       BlitBitmap(src_bitmap, bitmap,
14828                  TILEX + c * 8, TILEY + 12, 6, 10,
14829                  TILEX * 16 + TILEX * x + 10 + j * 8,
14830                  TILEY * y + 10 + yoffset_ge);
14831
14832       ii /= 10;
14833     }
14834   }
14835
14836   if (SDL_SaveBMP(bitmap->surface, dst_filename) != 0)
14837     Fail("cannot save CE graphics file '%s'", dst_filename);
14838
14839   FreeBitmap(bitmap);
14840
14841   CloseAllAndExit(0);
14842 }